Skip to content

Commit

Permalink
Merge branch 'refactor' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
monoxgas committed May 6, 2024
2 parents 217cd74 + 7c33d12 commit 457864b
Show file tree
Hide file tree
Showing 36 changed files with 4,010 additions and 1,001 deletions.
11 changes: 11 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,15 @@
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"yaml.schemas": {
"https://squidfunk.github.io/mkdocs-material/schema.json": "mkdocs.yml"
},
"yaml.customTags": [
"!ENV scalar",
"!ENV sequence",
"!relative scalar",
"tag:yaml.org,2002:python/name:material.extensions.emoji.to_svg",
"tag:yaml.org,2002:python/name:material.extensions.emoji.twemoji",
"tag:yaml.org,2002:python/name:pymdownx.superfences.fence_code_format"
]
}
310 changes: 25 additions & 285 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,303 +1,43 @@
# Rigging

Rigging is a lightweight LLM interaction framework built on Pydantic XML and LiteLLM. It supports useful primitives for validating LLM output and adding tool calling abilities to models that don't natively support it. It also has various helpers for common tasks like structured object parsing, templating chats, overloading generation parameters, stripping chat segments, and continuing conversations.
Rigging is a lightweight LLM interaction framework built on Pydantic XML. The goal is to make leveraging LLMs in production pipelines as simple and effictive as possible. Here are the highlights:

Modern python with type hints, pydantic validation, native serialization support, etc.
- **Structured Pydantic models** can be used interchangably with unstructured text output.
- LiteLLM as the default generator giving you **instant access to a huge array of models**.
- Add easy **tool calling** abilities to models which don't natively support it.
- Store different models and configs as **simple connection strings** just like databases.
- Chat templating, forking, continuations, generation parameter overloads, stripping segments, etc.
- Modern python with type hints, async support, pydantic validation, serialization, etc.

```
pip install rigging
```

### Basic Chats

```python
```py
import rigging as rg
from rigging.model import CommaDelimitedAnswer as Answer

generator = rg.get_generator("claude-2.1")
chat = generator.chat(
[
{"role": "system", "content": "You are a wizard harry."},
{"role": "user", "content": "Say hello!"},
]
).run()

print(chat.last)
# [assistant]: Hello!

print(f"{chat.last!r}")
# Message(role='assistant', parts=[], content='Hello!')

print(chat.prev)
# [
# Message(role='system', parts=[], content='You are a wizard harry.'),
# Message(role='user', parts=[], content='Say hello!'),
# ]

print(chat.json)
# [{ ... }]

```

### Model Parsing

```python
import rigging as rg

class Answer(rg.Model):
content: str

chat = (
rg.get_generator("claude-3-haiku-20240307")
.chat([
{"role": "user", "content": f"Say your name between {Answer.xml_tags()}."},
])
.until_parsed_as(Answer)
answer = rg.get_generator('gpt-4') \
.chat(f"Give me 3 famous authors between {Answer.xml_tags()} tags.") \
.until_parsed_as(Answer) \
.run()
)

answer = chat.last.parse(Answer)
print(answer.content)

# "Claude"

print(f"{chat.last!r}")

# Message(role='assistant', parts=[
# ParsedMessagePart(model=Answer(content='Claude'), ref='<answer>Claude</answer>')
# ], content='<Answer>Claude</Answer>')
print(answer.items)

chat.last.content = "new content" # Updating content strips parsed parts
print(f"{chat.last!r}")

# Message(role='assistant', parts=[], content='new content')
# ['J. R. R. Tolkien', 'Stephen King', 'George Orwell']
```

### Mutliple Models

```python
import rigging as rg

class Joke(rg.Model):
content: str

chat = (
rg.get_generator("claude-2.1")
.chat([{
"role": "user",
"content": f"Provide 3 short jokes each wrapped with {Joke.xml_tags()} tags."},
])
.run()
)

jokes = chat.last.parse_set(Joke)

# [
# Joke(content="Why don't eggs tell jokes? They'd crack each other up!"),
# Joke(content='What do you call a bear with no teeth? A gummy bear!'),
# Joke(content='What do you call a fake noodle? An Impasta!')
# ]
```

### Complex Models

```python
import rigging as rg

class Inner(rg.Model):
type: str = rg.attr()
content: str

class Outer(rg.Model):
name: str = rg.attr()
inners: list[Inner] = rg.element()

outer = Outer(name="foo", inners=[
Inner(type="cat", content="meow"),
Inner(type="dog", content="bark")
])

print(outer.to_pretty_xml())

# <outer name="foo">
# <inner type="cat">meow</inner>
# <inner type="dog">bark</inner>
# </outer>
```

### Tools

```python
from typing import Annotated
import rigging as rg

class WeatherTool(rg.Tool):
@property
def name(self) -> str:
return "weather"

@property
def description(self) -> str:
return "A tool to get the weather for a location"

def get_for_city(self, city: Annotated[str, "The city name to get weather for"]) -> str:
print(f"[=] get_for_city('{city}')")
return f"The weather in {city} is nice today"

chat = (
rg.get_generator("mistral/mistral-tiny")
.chat(
[
{"role": "user", "content": "What is the weather in London?"},
]
)
.using(WeatherTool(), force=True)
.run()
)

# [=] get_for_city('London')

print(chat.last.content)

# "Based on the information I've received, the weather in London is nice today."
```
Rigging is built and maintained by [dreadnode](https://dreadnode.io) where we use it daily for our work.

### Continuing Chats

```python
import rigging as rg

generator = rg.get_generator("gpt-3.5-turbo")
chat = generator.chat([
{"role": "user", "content": "Hello, how are you?"},
]).run()

print(chat.last.content)

# "Hello! I'm an AI language model, ..."

cont = chat.continue_(
{"role": "user", "content": "That's good, tell me a joke"}
).run()

print(cont.last.content)

# "Sure, here's a joke for you: ..."
```

### Basic Templating

```python
import rigging as rg

template = rg.get_generator("gpt-4").chat([
{"role": "user", "content": "What is the capitol of $country?"},
])

for country in ["France", "Germany"]:
print(template.apply(country=country).run().last)

# The capital of France is Paris.
# The capital of Germany is Berlin.
```

### Overload Generation Params

```python
import rigging as rg

pending = rg.get_generator("gpt-3.5-turbo,max_tokens=50").chat([
{"role": "user", "content": "Say a haiku about boats"},
])

for temp in [0.1, 0.5, 1.0]:
print(pending.overload(temperature=temp).run().last.content)

```

### Strip Parsed Sections

```python
import rigging as rg

class Reasoning(rg.Model):
content: str

meaning = rg.get_generator("claude-2.1").chat([
{
"role": "user",
"content": "What is the meaning of life in one sentence? "
f"Document your reasoning between {Reasoning.xml_tags()} tags.",
},
]).run()

# Gracefully handle mising models
reasoning = meaning.last.try_parse(Reasoning)
if reasoning:
print("reasoning:", reasoning.content.strip())

# Strip parsed content to avoid sharing
# previous thoughts with the model.
without_reasons = meaning.strip(Reasoning)
print("meaning of life:", without_reasons.last.content.strip())

# follow_up = without_thoughts.continue_(...)
```

### Custom Generator

Any custom generator simply needs to implement a `complete` function, and
then it can be used anywhere inside rigging.

```python
class Custom(Generator):
# model: str
# api_key: str
# params: GeneratorParams

custom_field: bool

def complete(
self,
messages: t.Sequence[rg.Message],
overloads: GenerateParams = GenerateParams(),
) -> rg.Message:
# Access self vars where needed
api_key = self.api_key
model_id = self.model

# Merge in args for API overloads
marged: dict[str, t.Any] = self._merge_params(overloads)

# response: str = ...

return rg.Message("assistant", response)


generator = Custom(model='foo', custom_field=True)
generator.chat(...)
## Installation
We publish every version to Pypi:
```bash
pip install rigging
```

*Note: we currently don't have anyway to "register" custom generators for `get_generator`.*

### Logging

By default rigging disables it's logger with loguru. To enable it run:

```python
from loguru import logger

logger.enable('rigging')
If you want to build from source:
```bash
cd rigging/
poetry install
```

To configure loguru terminal + file logging format overrides:

```python
from rigging.logging import configure_logging
## Getting Started

configure_logging(
'info', # stderr level
'out.log', # log file (optional)
'trace' # log file level
)
```
*(This will remove existing handlers, so you might prefer to configure them yourself)*
Head over to **[our documentation](https://rigging.dreadnode.io) for more information.
1 change: 1 addition & 0 deletions docs/api/chat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
::: rigging.chat
1 change: 1 addition & 0 deletions docs/api/completion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
::: rigging.completion
1 change: 1 addition & 0 deletions docs/api/error.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
::: rigging.error
1 change: 1 addition & 0 deletions docs/api/generator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
::: rigging.generator
1 change: 1 addition & 0 deletions docs/api/logging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
::: rigging.logging
1 change: 1 addition & 0 deletions docs/api/message.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
::: rigging.message
1 change: 1 addition & 0 deletions docs/api/model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
::: rigging.model
1 change: 1 addition & 0 deletions docs/api/parsing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
::: rigging.parsing
1 change: 1 addition & 0 deletions docs/api/tool.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
::: rigging.tool
Binary file added docs/assets/logo_black.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/logo_white.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 457864b

Please sign in to comment.