Implement DSL and documentation

This commit is contained in:
Jason Liu
2023-07-09 00:09:02 +08:00
committed by GitHub
parent f4754fb17c
commit 3498aa12d1
24 changed files with 648 additions and 146 deletions
+23 -16
View File
@@ -1,8 +1,7 @@
# Pydantic is all you need: An OpenAI Function Call Pydantic Integration Module
We try to provides a powerful and efficient approach to output parsing when interacting with OpenAI's Function Call API. One that is framework agnostic and minimizes any dependencies. It leverages the data validation capabilities of the Pydantic library to handle output parsing in a more structured and reliable manner.
If you have any feedback, leave an issue or hit me up on [twitter](https://twitter.com/jxnlco).
If you have any feedback, leave an issue or hit me up on [twitter](https://twitter.com/jxnlco).
This repo also contains a range of examples I've used in experimetnation and in production and I welcome new contributions for different types of schemas.
@@ -128,9 +127,10 @@ class UserDetails(BaseModel):
```python
from openai_function_call import OpenAISchema
from openai_function_call.dsl import ChatCompletion, MultiTask, messages as m
from openai_function_call.dsl.messages import system as s
# Define a subtask you'd like to extract from then,
# We'll use MultTask to easily map it to a List[Search]
# We'll use MultTask to easily map it to a List[Search]
# so we can extract more than one
class Search(OpenAISchema):
id: int
@@ -138,24 +138,31 @@ class Search(OpenAISchema):
tasks = (
ChatCompletion(name="Acme Inc Email Segmentation", model="gpt-3.5-turbo-0613")
| m.ExpertSystem(task="Segment emails into search queries")
| s.Identity(identity="World class state of the art agent") # if no identity is provided, this is the default one
| s.Task(task="Segment emails into search queries")
| s.Style(style="Professional, clear and concise")
| s.Guidelines(guidelines=[
'You never swear',
'You are polite',
'You say please and thank you often.'
])
| s.Tips(tips=[
"When unsure about the correct segmentation, try to think about the task as a whole",
"If acronyms are used expand them to their full form",
"Use multiple phrases to describe the same thing"]
)
| MultiTask(subtask_class=Search)
| m.TaggedMessage(
tag="email",
content="Can you find the video I sent last week and also the post about dogs",
)
| m.TipsMessage(
tips=[
"When unsure about the correct segmentation, try to think about the task as a whole",
"If acronyms are used expand them to their full form",
"Use multiple phrases to describe the same thing",
]
)
| m.ChainOfThought()
)
# Its important that this just builds you request,
# all these | operators are overloaded and all we do is compile
# it to the openai kwargs
# Also note that the System components are combined sequentially
# so the order matters!
assert isinstance(tasks, ChatCompletion)
pprint(tasks.kwargs, indent=3)
"""
@@ -163,14 +170,15 @@ pprint(tasks.kwargs, indent=3)
"messages": [
{
"role": "system",
"content": "You are a world class, state of the art agent capable
of correctly completing the task: `Segment emails into search queries`"
"content": "You are a world class state of the art agent.\n\nYour purpose is to correctly complete this task:
`Segment emails into search queries`.\n\nYour style when answering is professional, clear and concise\n\n
These are the guidelines you consider when completing your task:\n\n* You never swear\n* You are polite\n* You say please and thank you often.\n\nHere are some tips to help you complete the task:\n\n* When unsure about the correct segmentation, try to think about the task as a whole\n* If acronyms are used expand them to their full form\n* Use multiple phrases to describe the same thing"
},
...
{
"role": "user",
"content": "<email>Can you find the video I sent last week and also the post about dogs</email>"
},
...
{
"role": "assistant",
"content": "Lets think step by step to get the correct answer:"
@@ -207,14 +215,13 @@ pprint(tasks.kwargs, indent=3)
"max_tokens": 1000,
"temperature": 0.1,
"model": "gpt-3.5-turbo-0613"
}
"""
# Once we call .create we'll be returned with a multitask object that contains our list of task
result = tasks.create()
for task in result.tasks:
# We can now extract the list of tasks as we could normally
# We can now extract the list of tasks as we could normally
assert isinstance(task, Search)
```
+18
View File
@@ -0,0 +1,18 @@
# Using the Chatcompletion
To get started with this api we must first instantiate a `ChatCompletion` object and build the api call
by piping messages and functions to it.
::: openai_function_call.dsl.completion
## Messages Types
The basis of a message is defined as a `dataclass`. However we provide helper functions and classes that provide additional functionality in the form of templates.
::: openai_function_call.dsl.messages.base
## Helper Messages / Templates
::: openai_function_call.dsl.messages.messages
::: openai_function_call.dsl.messages.user
+5
View File
@@ -0,0 +1,5 @@
# MultiTask
We define a helper function `MultiTask` that dynamitcally creates a new schema that has a task attribute defined as a list of the task subclass, it including some prebuild prompts and allows us to avoid writing some extra code.
::: openai_function_call.dsl.multitask
+1 -1
View File
@@ -55,4 +55,4 @@ class UserDetails(BaseModel):
## Code Reference
::: openai_function_call
::: openai_function_call.function_calls
+126
View File
@@ -0,0 +1,126 @@
# Using the pipeline
The pipeapi is some syntactic sugar to help build prompts in a readable way that avoids having to remember best practices around wording and structure. Examples include adding tips, tagging data with xml, or even including the chain of thought prompt as an assistant message.
### Example Pipeline
```python
from openai_function_call import OpenAISchema, dsl
from pydantic import Field
class SearchQuery(OpenAISchema):
query: str = Field(
...,
description="Detailed, comprehensive, and specific query to be used for semantic search",
)
SearchResponse = dsl.MultiTask(
subtask_class=SearchQuery,
)
task = (
dsl.ChatCompletion(name="Segmenting Search requests example")
| dsl.SystemTask(task="Segment search results")
| dsl.TaggedMessage(
content="can you send me the data about the video investment and the one about spot the dog?",
tag="query",
)
| dsl.TipsMessage(
tips=[
"Expand query to contain multiple forms of the same word (SSO -> Single Sign On)",
"Use the title to explain what the query should return, but use the query to complete the search",
"The query should be detailed, specific, and cast a wide net when possible",
]
)
| SearchResponse
)
search_request = task.create() # type: ignore
assert isinstance(search_request, SearchResponse)
print(search_request.json(indent=2))
```
Output
```json
{
"tasks": [
{
"query": "data about video investment"
},
{
"query": "data about spot the dog"
}
]
}
```
## Inspecting the API Call
To make it easy for you to understand what this api is doing we default only construct the kwargs for the chat completion call.
```python
print(task.kwargs)
```
```json
{
"messages": [
{
"role": "system",
"content": "You are a world class state of the art algorithm capable of correctly completing the following task: `Segment search results`."
},
{
"role": "user",
"content": "Consider the following data:\n\n<query>can you send me the data about the video investment and the one about spot the dog?</query>"
},
{
"role": "user",
"content": "Here are some tips to help you complete the task:\n\n* Expand query to contain multiple forms of the same word (SSO -> Single Sign On)\n* Use the title to explain what the query should return, but use the query to complete the search\n* The query should be detailed, specific, and cast a wide net when possible"
}
],
"functions": [
{
"name": "MultiSearchQuery",
"description": "Correctly segmented set of search queries",
"parameters": {
"type": "object",
"properties": {
"tasks": {
"description": "Correctly segmented list of `SearchQuery` tasks",
"type": "array",
"items": {
"$ref": "#/definitions/SearchQuery"
}
}
},
"definitions": {
"SearchQuery": {
"type": "object",
"properties": {
"query": {
"description": "Detailed, comprehensive, and specific query to be used for semantic search",
"type": "string"
}
},
"required": [
"query"
]
}
},
"required": [
"tasks"
]
}
}
],
"function_call": {
"name": "MultiSearchQuery"
},
"max_tokens": 1000,
"temperature": 0.1,
"model": "gpt-3.5-turbo-0613"
}
```
View File
View File
+42
View File
@@ -0,0 +1,42 @@
from fastapi import FastAPI
from openai_function_call import OpenAISchema
import openai_function_call.dsl as dsl
from pydantic import BaseModel, Field
app = FastAPI(title="Example Application using openai_function_call")
class SearchRequest(BaseModel):
body: str
class SearchQuery(OpenAISchema):
title: str = Field(..., description="Question that the query answers")
query: str = Field(
...,
description="Detailed, comprehensive, and specific query to be used for semantic search",
)
SearchResponse = dsl.MultiTask(
subtask_class=SearchQuery,
description="Correctly segmented set of search queries",
)
@app.post("/search", response_model=SearchResponse)
async def search(request: SearchRequest):
task = (
dsl.ChatCompletion(name="Segmenting Search requests example")
| dsl.SystemTask(task="Segment search results")
| dsl.TaggedMessage(content=request.body, tag="query")
| dsl.TipsMessage(
tips=[
"Expand query to contain multiple forms of the same word (SSO -> Single Sign On)",
"Use the title to explain what the query should return, but use the query to complete the search",
"The query should be detailed, specific, and cast a wide net when possible",
]
)
| SearchRequest
)
return await task.acreate()
+50
View File
@@ -0,0 +1,50 @@
from openai_function_call import OpenAISchema, dsl
from pydantic import Field
class SearchQuery(OpenAISchema):
query: str = Field(
...,
description="Detailed, comprehensive, and specific query to be used for semantic search",
)
SearchResponse = dsl.MultiTask(
subtask_class=SearchQuery,
description="Correctly segmented set of search queries",
)
task = (
dsl.ChatCompletion(name="Segmenting Search requests example")
| dsl.SystemTask(task="Segment search results")
| dsl.TaggedMessage(
content="can you send me the data about the video investment and the one about spot the dog?",
tag="query",
)
| dsl.TipsMessage(
tips=[
"Expand query to contain multiple forms of the same word (SSO -> Single Sign On)",
"Use the title to explain what the query should return, but use the query to complete the search",
"The query should be detailed, specific, and cast a wide net when possible",
]
)
| SearchResponse
)
import pprint
import json
print(json.dumps(task.kwargs, indent=1))
"""
{
"tasks": [
{
"query": "data about video investment"
},
{
"query": "data about spot the dog"
}
]
}
"""
+5 -2
View File
@@ -31,7 +31,10 @@ markdown_extensions:
- md_in_html
nav:
- Home: 'index.md'
- Module:
- 'Schemas': 'openai_schema.md'
- API Reference:
- 'OpenAISchema': 'openai_schema.md'
- "Helper: MultiTask": "multitask.md"
- "Example: Pipeline API": "pipeline-example.md"
- "Docs": "chat-completion.md"
- Examples:
- 'Missing': 'help.md'
+2 -1
View File
@@ -1,3 +1,4 @@
from .function_calls import OpenAISchema, openai_function, openai_schema
from .dsl.multitask import MultiTask
__all__ = ["OpenAISchema", "openai_function", "openai_schema"]
__all__ = ["OpenAISchema", "openai_function", "MultiTask", "openai_schema"]
+1 -1
View File
@@ -1,5 +1,5 @@
from .completion import ChatCompletion
from .multitask import MultiTask
from .messages import *
from .multitask import MultiTask
__all__ = ["ChatCompletion", "MultiTask", "messages"]
+89 -29
View File
@@ -1,28 +1,63 @@
from typing import List, Optional, Type, Union
from pydantic import BaseModel, Field, create_model
import openai
from typing import List, Union
from pydantic import BaseModel, Field
from openai_function_call import OpenAISchema
from .messages import (
Message,
SystemMessage,
ChainOfThought,
ExpertSystem,
TaggedMessage,
TipsMessage,
)
from .messages import ChainOfThought, Message, MessageRole, SystemMessage
class ChatCompletion(BaseModel):
"""
A chat completion is a collection of messages and configration options that can be used to
generate a chat response from the OpenAI API.
Usage:
In order to generate a chat response from the OpenAI API, you need to create a chat completion and then pipe it to a message and a `OpenAISchema`. Then when `create` or `acreate` is called we'll return the response from the API as an instance of `OpenAISchema`.
Example:
```python
class Sum(OpenAISchema):
a: int
b: int
completion = (
ChatCompletion("example")
| TaggedMessage(content="What is 1 + 1?", tag="question")
| Schema
)
print(completion.create())
# Sum(a=1, b=1)
```
Tips:
* You can use the `|` operator to chain multiple messages and functions together
* There should be exactly one function call class (OpenAISchema) per chat completion
* System messages will be concatenated together
* Only one chain of thought message can be used per completion
Attributes:
name (str): The name of the chat completion
model (str): The model to use for the chat completion (default: "gpt-3.5-turbo-0613")
max_tokens (int): The maximum number of tokens to generate (default: 1000)
temperature (float): The temperature to use for the chat completion (default: 0.1)
stream (bool): Whether to stream the response from the API (default: False)
Warning:
Currently we do not support streaming the response from the API, so the stream parameter is not supported yet.
"""
name: str
model: str = Field(default="gpt3.5-turbo-0613")
model: str = Field(default="gpt-3.5-turbo-0613")
max_tokens: int = Field(default=1000)
temperature: float = Field(default=0.1)
stream: bool = Field(default=False)
messages: List[Message] = Field(default_factory=list, repr=False)
system_message: SystemMessage = Field(default=None, repr=False)
system_message: Message = Field(default=None, repr=False)
cot_message: ChainOfThought = Field(default=None, repr=False)
function: OpenAISchema = Field(default=None, repr=False)
@@ -30,19 +65,25 @@ class ChatCompletion(BaseModel):
assert self.stream == False, "Stream is not supported yet"
def __or__(self, other: Union[Message, OpenAISchema]) -> "ChatCompletion":
if isinstance(other, Message):
if isinstance(other, SystemMessage):
if self.system_message:
self.system_message.content += "\n\n" + other.content
self.system_message = other
"""
Add a message or function to the chat completion, this can be used to chain multiple messages and functions together. It should contain some set of user or system messages along with a function call class (OpenAISchema)
if isinstance(other, ChainOfThought):
if self.cot_message:
raise ValueError(
"Only one chain of thought message can be used per completion"
)
self.cot_message = other
self.messages.append(other)
"""
if isinstance(other, Message):
if other.role == MessageRole.SYSTEM:
if not self.system_message:
self.system_message = other # type: ignore
else:
self.system_message.content += "\n\n" + other.content
else:
if isinstance(other, ChainOfThought):
if self.cot_message:
raise ValueError(
"Only one chain of thought message can be used per completion"
)
self.cot_message = other
self.messages.append(other)
else:
if self.function:
raise ValueError(
@@ -51,13 +92,21 @@ class ChatCompletion(BaseModel):
self.function = other
assert self.model not in {
"gpt3.5-turbo",
"gpt4",
"gpt-3.5-turbo",
"gpt-4",
}, "Only *-0613 models can currently use functions"
return self
@property
def kwargs(self) -> dict:
"""
Construct the kwargs for the OpenAI API call
Example:
```python
result = openai.ChatCompletion.create(**self.kwargs)
```
"""
kwargs = {}
messages = []
@@ -91,15 +140,26 @@ class ChatCompletion(BaseModel):
return kwargs
def create(self):
"""
Create a chat response from the OpenAI API
Returns:
response (OpenAISchema): The response from the OpenAI API
"""
kwargs = self.kwargs
completion = openai.ChatCompletion.create(**kwargs)
if self.function:
return self.function.from_response(completion)
async def acreate(self):
"""
Create a chat response from the OpenAI API asynchronously
Returns:
response (OpenAISchema): The response from the OpenAI API
"""
kwargs = self.kwargs
completion = openai.ChatCompletion.acreate(**kwargs)
if self.function:
return self.function.from_response(await completion)
return await completion
-81
View File
@@ -1,81 +0,0 @@
from enum import Enum, auto
from pydantic.dataclasses import dataclass
from pydantic import Field
from typing import Optional, List
class MessageRole(Enum):
USER = auto()
SYSTEM = auto()
ASSISTANT = auto()
@dataclass
class Message:
content: str = Field(default=None, repr=True)
role: MessageRole = Field(default=MessageRole.USER, repr=False)
name: Optional[str] = Field(default=None)
def dict(self):
assert self.content is not None, "Content must be set!"
obj = {
"role": self.role.name.lower(),
"content": self.content,
}
if self.name and self.role == MessageRole.USER:
obj["name"] = self.name
return obj
@dataclass
class SystemMessage(Message):
def __post_init__(self):
self.role = MessageRole.SYSTEM
@dataclass
class UserMessage(Message):
def __post_init__(self):
self.role = MessageRole.USER
@dataclass
class TaggedMessage(Message):
tag: str = Field(default="data", repr=True)
def __post_init__(self):
self.role = MessageRole.USER
self.content = f"<{self.tag}>{self.content}</{self.tag}>"
@dataclass
class AssistantMessage(Message):
def __post_init__(self):
self.role = MessageRole.ASSISTANT
@dataclass
class ExpertSystem(Message):
task: str = Field(default=None, repr=True)
def __post_init__(self):
self.role = MessageRole.SYSTEM
self.content = f"You are a world class, state of the art agent capable of correctly completing the task: `{self.task}`"
@dataclass
class TipsMessage(Message):
tips: List[str] = Field(default_factory=list)
header: str = "Here are some tips to help you complete the task"
def __post_init__(self):
self.role = MessageRole.USER
tips = "\n* ".join(self.tips)
self.content = f"{self.header}:\n\n* {tips}"
@dataclass
class ChainOfThought(Message):
def __post_init__(self):
self.role = MessageRole.ASSISTANT
self.content = "Lets think step by step to get the correct answer:"
@@ -0,0 +1,26 @@
from .base import Message, MessageRole
from .messages import (
SystemMessage,
SystemGuidelines,
SystemIdentity,
SystemStyle,
SystemTask,
SystemTips,
ChainOfThought,
)
from .user import TaggedMessage, TipsMessage, UserMessage
__all__ = [
"Message",
"MessageRole",
"ChainOfThought",
"UserMessage",
"TaggedMessage",
"TipsMessage",
"SystemMessage",
"SystemGuidelines",
"SystemIdentity",
"SystemStyle",
"SystemTask",
"SystemTips",
]
+58
View File
@@ -0,0 +1,58 @@
from enum import Enum, auto
from typing import Optional
from pydantic import Field
from pydantic.dataclasses import dataclass
class MessageRole(Enum):
"""
An enum that represents the role of a message.
Attributes:
USER: A message from the user.
SYSTEM: A message from the system.
ASSISTANT: A message from the assistant.
"""
USER = auto()
SYSTEM = auto()
ASSISTANT = auto()
@dataclass
class Message:
"""
A message class that helps build messages for the chat interface.
Attributes:
content (str): The content of the message.
role (MessageRole): The role of the message.
name (Optional[str]): The name of the user, only used if the role is USER.
Tips:
If you want to make custom messages simple make a function that returns the `Message` class and use that as part of your pipes. For example if you want to add additional context:
```python
def GetUserData(user_id) -> Message:
data = ...
return Message(
content="This is some more user data: {data} for {user_id}
role=MessageRole.USER
)
```
"""
content: str = Field(default=None, repr=True)
role: MessageRole = Field(default=MessageRole.USER, repr=False)
name: Optional[str] = Field(default=None)
def dict(self):
assert self.content is not None, "Content must be set!"
obj = {
"role": self.role.name.lower(),
"content": self.content,
}
if self.name and self.role == MessageRole.USER:
obj["name"] = self.name
return obj
@@ -0,0 +1,108 @@
from typing import List
from .base import Message, MessageRole
from pydantic.dataclasses import dataclass
def SystemIdentity(identity: str) -> Message:
"""
Create a system message that tells the user what their identity is.
Parameters:
identity (str): The identity of the user.
Returns:
message (Message): A system message that tells the user what their identity is.
"""
return Message(content=f"You are a {identity.lower()}.", role=MessageRole.SYSTEM)
def SystemTask(task: str) -> Message:
"""
Create a system message that tells the user what task they are doing, uses language to
push the system to behave as a world class algorithm.
Parameters:
task (str): The task the user is doing.
Returns:
message (Message): A system message that tells the user what task they are doing.
"""
return Message(
content=f"You are a world class state of the art algorithm capable of correctly completing the following task: `{task}`.",
role=MessageRole.SYSTEM,
)
def SystemStyle(style: str) -> Message:
"""
Create a system message that tells the user what style they are responding in.
Parameters:
style (str): The style the user is responding in.
Returns:
message (Message): A system message that tells the user what style they are responding in.
"""
return Message(
content=f"You must respond with in following style: {style.lower()}.",
role=MessageRole.SYSTEM,
)
def SystemMessage(content: str) -> Message:
"""
Create a system message.
Parameters:
content (str): The content of the message.
Returns:
message (Message): A system message."""
return Message(content=content, role=MessageRole.SYSTEM)
def SystemGuidelines(guidelines: List[str]) -> Message:
"""
Create a system message that tells the user what guidelines they must follow when responding.
Parameters:
guidelines (List[str]): The guidelines the user must follow when responding.
Returns:
message (Message): A system message that tells the user what guidelines they must follow when responding.
"""
guideline_str = "\n* ".join(guidelines)
return Message(
content=f"Here are the guidelines you must to follow when responding:\n\n* {guideline_str}",
role=MessageRole.SYSTEM,
)
def SystemTips(tips: List[str]) -> Message:
"""
Create a system message that gives the user some tips before responding.
Parameters:
tips (List[str]): The tips the user should follow when responding.
Returns:
message (Message): A system message that gives the user some tips before responding.
"""
tips_str = "\n* ".join(tips)
return Message(
content=f"Here are some tips before responding:\n\n* {tips_str}",
role=MessageRole.SYSTEM,
)
@dataclass
class ChainOfThought(Message):
"""
Special message type to correctly leverage chain of thought reasoning
for the task. This is automatically set as the last message.
"""
def __post_init__(self):
self.content = "Lets think step by step to get the correct answer:"
self.role = MessageRole.ASSISTANT
+54
View File
@@ -0,0 +1,54 @@
from typing import List
from .base import Message, MessageRole
def TipsMessage(
tips: List[str], header: str = "Here are some tips to help you complete the task"
) -> Message:
"""
Create a system message that gives the user tips to help them complete the task.
Parameters:
tips (List[str]): A list of tips to help the user complete the task.
header (str): The header of the message.
Returns:
message (Message): A user message that gives the user tips to help them complete the
"""
tips_str = "\n* ".join(tips)
return Message(
content=f"{header}:\n\n* {tips_str}",
role=MessageRole.USER,
)
def UserMessage(content: str) -> Message:
"""
Create a user message.
Parameters:
content (str): The content of the message.
Returns:
message (Message): A user message.
"""
return Message(content=content, role=MessageRole.USER)
def TaggedMessage(
content: str, tag: str = "data", header: str = "Consider the following data:"
) -> Message:
"""
Create a user message.
Parameters:
content (str): The content of the message.
tag (str): The tag to use, will show up as <tag>content</tag>.
header (str): The header to reference the data
Returns:
message (Message): A user message with the data tagged.
"""
content = f"{header}\n\n<{tag}>{content}</{tag}>"
return Message(content=content, role=MessageRole.USER)
+26 -4
View File
@@ -14,11 +14,33 @@ def MultiTask(
for a specific task, names and descriptions are automatically generated. However
they can be overridden.
:param subtask_class: The base class to use for the MultiTask
:param name: The name of the MultiTask
:param description: The description of the MultiTask
Note:
Using this function is equivalent to creating a class that inherits from
OpenAISchema and has a list of the subtask class as a field.
```python
class MultiTask(OpenAISchema):
\"""
Correct segmentation of `{subtask_class.__name__}` tasks
\"""
tasks: List[subtask_class] = Field(
default_factory=list,
repr=False,
description=f"Correctly segmented list of `{subtask_class.__name__}` tasks",
)
```
Parameters:
subtask_class (Type[OpenAISchema]): The base class to use for the MultiTask
name (Optional[str]): The name of the MultiTask class, if None then the name
of the subtask class is used as `Multi{subtask_class.__name__}`
description (Optional[str]): The description of the MultiTask class, if None
then the description is set to `Correct segmentation of `{subtask_class.__name__}` tasks`
Returns:
OpenAISchema: A new class that can be used to segment multiple tasks
:return: new schema class called `Multi{subtask_class.name}`
"""
task_name = subtask_class.__name__ if name is None else name
+2 -2
View File
@@ -22,8 +22,8 @@
import json
from functools import wraps
from typing import Any, Callable
from pydantic import validate_arguments, BaseModel
from typing import Any, Callable, Optional, List, Type
from pydantic import validate_arguments, BaseModel, create_model, Field
def _remove_a_key(d, remove_key) -> None:
@@ -1,5 +1,7 @@
from openai_function_call import OpenAISchema
from openai_function_call.dsl import ChatCompletion, MultiTask, messages as m
from openai_function_call import OpenAISchema, MultiTask
from openai_function_call.dsl import ChatCompletion
from openai_function_call.dsl import messages as m
from openai_function_call.dsl.messages import system as s
def test_chatcompletion_has_kwargs():
@@ -9,7 +11,7 @@ def test_chatcompletion_has_kwargs():
task = (
ChatCompletion(name="Acme Inc Email Segmentation", model="gpt3.5-turbo-0613")
| m.ExpertSystem(task="Segment emails into search queries")
| s.SystemTask(task="Segment emails into search queries")
| MultiTask(subtask_class=Search)
| m.TaggedMessage(
tag="email",
+1 -1
View File
@@ -21,7 +21,7 @@ def test_openai_schema():
assert hasattr(Dataframe, "openai_schema")
assert hasattr(Dataframe, "from_response")
assert hasattr(Dataframe, "to_pandas")
assert Dataframe.openai_schema["name"] == "Dataframe"
assert Dataframe.openai_schema["name"] == "Dataframe" # type: ignore
def test_openai_schema_raises_error():
+4 -3
View File
@@ -1,4 +1,5 @@
from openai_function_call.dsl import messages as m
from openai_function_call.dsl.messages import system as s
def test_create_message():
@@ -41,10 +42,10 @@ def test_create_tagged_message():
}
def test_expert_system_message():
assert m.ExpertSystem(task="task").dict() == {
def test_task_message():
assert s.SystemTask(task="task").dict() == {
"role": "system",
"content": "You are a world class, state of the art agent capable of correctly completing the task: `task`",
"content": f"You are a world class state of the art algorithm capable of correctly completing the following task: `task`.",
}
+2 -2
View File
@@ -1,5 +1,5 @@
from openai_function_call.dsl import MultiTask
from openai_function_call import OpenAISchema
from openai_function_call.dsl.multitask import MultiTask
def test_multi_task():
@@ -9,7 +9,7 @@ def test_multi_task():
id: int
query: str
multitask = MultiTask(subtask_class=Search)
multitask = MultiTask(Search)
assert multitask.openai_schema == {
"description": "Correct segmentation of `Search` tasks",
"name": "MultiSearch",