Merge branch 'main' into patch-1
This commit is contained in:
commit
03869a75ac
12 changed files with 80 additions and 8 deletions
|
|
@ -140,7 +140,14 @@ The Agents SDK is designed to be highly flexible, allowing you to model a wide r
|
|||
|
||||
## Tracing
|
||||
|
||||
The Agents SDK automatically traces your agent runs, making it easy to track and debug the behavior of your agents. Tracing is extensible by design, supporting custom spans and a wide variety of external destinations, including [Logfire](https://logfire.pydantic.dev/docs/integrations/llms/openai/#openai-agents), [AgentOps](https://docs.agentops.ai/v1/integrations/agentssdk), [Braintrust](https://braintrust.dev/docs/guides/traces/integrations#openai-agents-sdk) and [Opik](https://www.comet.com/docs/opik/tracing/integrations/openai_agents). For more details about how to customize or disable tracing, see [Tracing](http://openai.github.io/openai-agents-python/tracing).
|
||||
The Agents SDK automatically traces your agent runs, making it easy to track and debug the behavior of your agents. Tracing is extensible by design, supporting custom spans and a wide variety of external destinations, including:
|
||||
- [AgentOps](https://docs.agentops.ai/v1/integrations/agentssdk)
|
||||
- [Braintrust](https://braintrust.dev/docs/guides/traces/integrations#openai-agents-sdk)
|
||||
- [Comet Opik](https://www.comet.com/docs/opik/tracing/integrations/openai_agents)
|
||||
- [Keywords AI](https://docs.keywordsai.co/integration/development-frameworks/openai-agent)
|
||||
- [Logfire](https://logfire.pydantic.dev/docs/integrations/llms/openai/#openai-agents)
|
||||
|
||||
For more details about how to customize or disable tracing, see [Tracing](http://openai.github.io/openai-agents-python/tracing).
|
||||
|
||||
## Development (only needed if you need to edit the SDK/examples)
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ The most common properties of an agent you'll configure are:
|
|||
```python
|
||||
from agents import Agent, ModelSettings, function_tool
|
||||
|
||||
@function_tool
|
||||
def get_weather(city: str) -> str:
|
||||
return f"The weather in {city} is sunny"
|
||||
|
||||
|
|
@ -20,7 +21,7 @@ agent = Agent(
|
|||
name="Haiku agent",
|
||||
instructions="Always respond in haiku form",
|
||||
model="o3-mini",
|
||||
tools=[function_tool(get_weather)],
|
||||
tools=[get_weather],
|
||||
)
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ class UserInfo: # (1)!
|
|||
name: str
|
||||
uid: int
|
||||
|
||||
@function_tool
|
||||
async def fetch_user_age(wrapper: RunContextWrapper[UserInfo]) -> str: # (2)!
|
||||
return f"User {wrapper.context.name} is 47 years old"
|
||||
|
||||
|
|
@ -44,7 +45,7 @@ async def main():
|
|||
|
||||
agent = Agent[UserInfo]( # (4)!
|
||||
name="Assistant",
|
||||
tools=[function_tool(fetch_user_age)],
|
||||
tools=[fetch_user_age],
|
||||
)
|
||||
|
||||
result = await Runner.run(
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ async def main():
|
|||
# San Francisco
|
||||
|
||||
# Second turn
|
||||
new_input = output.to_input_list() + [{"role": "user", "content": "What state is it in?"}]
|
||||
new_input = result.to_input_list() + [{"role": "user", "content": "What state is it in?"}]
|
||||
result = await Runner.run(agent, new_input)
|
||||
print(result.final_output)
|
||||
# California
|
||||
|
|
|
|||
|
|
@ -94,3 +94,4 @@ External trace processors include:
|
|||
- [Braintrust](https://braintrust.dev/docs/guides/traces/integrations#openai-agents-sdk)
|
||||
- [Pydantic Logfire](https://logfire.pydantic.dev/docs/integrations/llms/openai/#openai-agents)
|
||||
- [AgentOps](https://docs.agentops.ai/v1/integrations/agentssdk)
|
||||
- [Keywords AI](https://docs.keywordsai.co/integration/development-frameworks/openai-agent)
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ async def math_guardrail(
|
|||
|
||||
return GuardrailFunctionOutput(
|
||||
output_info=final_output,
|
||||
tripwire_triggered=not final_output.is_math_homework,
|
||||
tripwire_triggered=final_output.is_math_homework,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ class InputGuardrail(Generic[TContext]):
|
|||
[RunContextWrapper[TContext], Agent[Any], str | list[TResponseInputItem]],
|
||||
MaybeAwaitable[GuardrailFunctionOutput],
|
||||
]
|
||||
"""A function that receives the the agent input and the context, and returns a
|
||||
"""A function that receives the agent input and the context, and returns a
|
||||
`GuardrailResult`. The result marks whether the tripwire was triggered, and can optionally
|
||||
include information about the guardrail's output.
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -10,15 +10,34 @@ class ModelSettings:
|
|||
|
||||
This class holds optional model configuration parameters (e.g. temperature,
|
||||
top_p, penalties, truncation, etc.).
|
||||
|
||||
Not all models/providers support all of these parameters, so please check the API documentation
|
||||
for the specific model and provider you are using.
|
||||
"""
|
||||
|
||||
temperature: float | None = None
|
||||
"""The temperature to use when calling the model."""
|
||||
|
||||
top_p: float | None = None
|
||||
"""The top_p to use when calling the model."""
|
||||
|
||||
frequency_penalty: float | None = None
|
||||
"""The frequency penalty to use when calling the model."""
|
||||
|
||||
presence_penalty: float | None = None
|
||||
"""The presence penalty to use when calling the model."""
|
||||
|
||||
tool_choice: Literal["auto", "required", "none"] | str | None = None
|
||||
"""The tool choice to use when calling the model."""
|
||||
|
||||
parallel_tool_calls: bool | None = False
|
||||
"""Whether to use parallel tool calls when calling the model."""
|
||||
|
||||
truncation: Literal["auto", "disabled"] | None = None
|
||||
"""The truncation strategy to use when calling the model."""
|
||||
|
||||
max_tokens: int | None = None
|
||||
"""The maximum number of output tokens to generate."""
|
||||
|
||||
def resolve(self, override: ModelSettings | None) -> ModelSettings:
|
||||
"""Produce a new ModelSettings by overlaying any non-None values from the
|
||||
|
|
@ -33,4 +52,5 @@ class ModelSettings:
|
|||
tool_choice=override.tool_choice or self.tool_choice,
|
||||
parallel_tool_calls=override.parallel_tool_calls or self.parallel_tool_calls,
|
||||
truncation=override.truncation or self.truncation,
|
||||
max_tokens=override.max_tokens or self.max_tokens,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -503,6 +503,7 @@ class OpenAIChatCompletionsModel(Model):
|
|||
top_p=self._non_null_or_not_given(model_settings.top_p),
|
||||
frequency_penalty=self._non_null_or_not_given(model_settings.frequency_penalty),
|
||||
presence_penalty=self._non_null_or_not_given(model_settings.presence_penalty),
|
||||
max_tokens=self._non_null_or_not_given(model_settings.max_tokens),
|
||||
tool_choice=tool_choice,
|
||||
response_format=response_format,
|
||||
parallel_tool_calls=parallel_tool_calls,
|
||||
|
|
@ -808,6 +809,13 @@ class _Converter:
|
|||
"content": cls.extract_text_content(content),
|
||||
}
|
||||
result.append(msg_developer)
|
||||
elif role == "assistant":
|
||||
flush_assistant_message()
|
||||
msg_assistant: ChatCompletionAssistantMessageParam = {
|
||||
"role": "assistant",
|
||||
"content": cls.extract_text_content(content),
|
||||
}
|
||||
result.append(msg_assistant)
|
||||
else:
|
||||
raise UserError(f"Unexpected role in easy_input_message: {role}")
|
||||
|
||||
|
|
|
|||
|
|
@ -235,6 +235,7 @@ class OpenAIResponsesModel(Model):
|
|||
temperature=self._non_null_or_not_given(model_settings.temperature),
|
||||
top_p=self._non_null_or_not_given(model_settings.top_p),
|
||||
truncation=self._non_null_or_not_given(model_settings.truncation),
|
||||
max_output_tokens=self._non_null_or_not_given(model_settings.max_tokens),
|
||||
tool_choice=tool_choice,
|
||||
parallel_tool_calls=parallel_tool_calls,
|
||||
stream=stream,
|
||||
|
|
|
|||
|
|
@ -216,5 +216,3 @@ class RunResultStreaming(RunResultBase):
|
|||
|
||||
if self._output_guardrails_task and not self._output_guardrails_task.done():
|
||||
self._output_guardrails_task.cancel()
|
||||
self._output_guardrails_task.cancel()
|
||||
self._output_guardrails_task.cancel()
|
||||
|
|
|
|||
|
|
@ -393,3 +393,38 @@ def test_unknown_object_errors():
|
|||
with pytest.raises(UserError, match="Unhandled item type or structure"):
|
||||
# Purposely ignore the type error
|
||||
_Converter.items_to_messages([TestObject()]) # type: ignore
|
||||
|
||||
|
||||
def test_assistant_messages_in_history():
|
||||
"""
|
||||
Test that assistant messages are added to the history.
|
||||
"""
|
||||
messages = _Converter.items_to_messages(
|
||||
[
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Hello",
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Hello?",
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "What was my Name?",
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
assert messages == [
|
||||
{"role": "user", "content": "Hello"},
|
||||
{"role": "assistant", "content": "Hello?"},
|
||||
{"role": "user", "content": "What was my Name?"},
|
||||
]
|
||||
assert len(messages) == 3
|
||||
assert messages[0]["role"] == "user"
|
||||
assert messages[0]["content"] == "Hello"
|
||||
assert messages[1]["role"] == "assistant"
|
||||
assert messages[1]["content"] == "Hello?"
|
||||
assert messages[2]["role"] == "user"
|
||||
assert messages[2]["content"] == "What was my Name?"
|
||||
|
|
|
|||
Loading…
Reference in a new issue