openai-agents 0.0.1__py3-none-any.whl → 0.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of openai-agents might be problematic. Click here for more details.

Files changed (53) hide show
  1. agents/__init__.py +223 -0
  2. agents/_config.py +23 -0
  3. agents/_debug.py +17 -0
  4. agents/_run_impl.py +792 -0
  5. agents/_utils.py +61 -0
  6. agents/agent.py +159 -0
  7. agents/agent_output.py +144 -0
  8. agents/computer.py +107 -0
  9. agents/exceptions.py +63 -0
  10. agents/extensions/handoff_filters.py +67 -0
  11. agents/extensions/handoff_prompt.py +19 -0
  12. agents/function_schema.py +340 -0
  13. agents/guardrail.py +320 -0
  14. agents/handoffs.py +236 -0
  15. agents/items.py +246 -0
  16. agents/lifecycle.py +105 -0
  17. agents/logger.py +3 -0
  18. agents/model_settings.py +35 -0
  19. agents/models/__init__.py +0 -0
  20. agents/models/_openai_shared.py +34 -0
  21. agents/models/fake_id.py +5 -0
  22. agents/models/interface.py +107 -0
  23. agents/models/openai_chatcompletions.py +952 -0
  24. agents/models/openai_provider.py +65 -0
  25. agents/models/openai_responses.py +384 -0
  26. agents/result.py +220 -0
  27. agents/run.py +904 -0
  28. agents/run_context.py +26 -0
  29. agents/stream_events.py +58 -0
  30. agents/strict_schema.py +167 -0
  31. agents/tool.py +286 -0
  32. agents/tracing/__init__.py +97 -0
  33. agents/tracing/create.py +306 -0
  34. agents/tracing/logger.py +3 -0
  35. agents/tracing/processor_interface.py +69 -0
  36. agents/tracing/processors.py +261 -0
  37. agents/tracing/scope.py +45 -0
  38. agents/tracing/setup.py +211 -0
  39. agents/tracing/span_data.py +188 -0
  40. agents/tracing/spans.py +264 -0
  41. agents/tracing/traces.py +195 -0
  42. agents/tracing/util.py +17 -0
  43. agents/usage.py +22 -0
  44. agents/version.py +7 -0
  45. openai_agents-0.0.2.dist-info/METADATA +202 -0
  46. openai_agents-0.0.2.dist-info/RECORD +49 -0
  47. openai_agents-0.0.2.dist-info/licenses/LICENSE +21 -0
  48. openai-agents/example.py +0 -2
  49. openai_agents-0.0.1.dist-info/METADATA +0 -17
  50. openai_agents-0.0.1.dist-info/RECORD +0 -6
  51. openai_agents-0.0.1.dist-info/licenses/LICENSE +0 -20
  52. {openai-agents → agents/extensions}/__init__.py +0 -0
  53. {openai_agents-0.0.1.dist-info → openai_agents-0.0.2.dist-info}/WHEEL +0 -0
@@ -0,0 +1,211 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import threading
5
+ from typing import Any
6
+
7
+ from . import util
8
+ from .logger import logger
9
+ from .processor_interface import TracingProcessor
10
+ from .scope import Scope
11
+ from .spans import NoOpSpan, Span, SpanImpl, TSpanData
12
+ from .traces import NoOpTrace, Trace, TraceImpl
13
+
14
+
15
+ class SynchronousMultiTracingProcessor(TracingProcessor):
16
+ """
17
+ Forwards all calls to a list of TracingProcessors, in order of registration.
18
+ """
19
+
20
+ def __init__(self):
21
+ # Using a tuple to avoid race conditions when iterating over processors
22
+ self._processors: tuple[TracingProcessor, ...] = ()
23
+ self._lock = threading.Lock()
24
+
25
+ def add_tracing_processor(self, tracing_processor: TracingProcessor):
26
+ """
27
+ Add a processor to the list of processors. Each processor will receive all traces/spans.
28
+ """
29
+ with self._lock:
30
+ self._processors += (tracing_processor,)
31
+
32
+ def set_processors(self, processors: list[TracingProcessor]):
33
+ """
34
+ Set the list of processors. This will replace the current list of processors.
35
+ """
36
+ with self._lock:
37
+ self._processors = tuple(processors)
38
+
39
+ def on_trace_start(self, trace: Trace) -> None:
40
+ """
41
+ Called when a trace is started.
42
+ """
43
+ for processor in self._processors:
44
+ processor.on_trace_start(trace)
45
+
46
+ def on_trace_end(self, trace: Trace) -> None:
47
+ """
48
+ Called when a trace is finished.
49
+ """
50
+ for processor in self._processors:
51
+ processor.on_trace_end(trace)
52
+
53
+ def on_span_start(self, span: Span[Any]) -> None:
54
+ """
55
+ Called when a span is started.
56
+ """
57
+ for processor in self._processors:
58
+ processor.on_span_start(span)
59
+
60
+ def on_span_end(self, span: Span[Any]) -> None:
61
+ """
62
+ Called when a span is finished.
63
+ """
64
+ for processor in self._processors:
65
+ processor.on_span_end(span)
66
+
67
+ def shutdown(self) -> None:
68
+ """
69
+ Called when the application stops.
70
+ """
71
+ for processor in self._processors:
72
+ logger.debug(f"Shutting down trace processor {processor}")
73
+ processor.shutdown()
74
+
75
+ def force_flush(self):
76
+ """
77
+ Force the processors to flush their buffers.
78
+ """
79
+ for processor in self._processors:
80
+ processor.force_flush()
81
+
82
+
83
+ class TraceProvider:
84
+ def __init__(self):
85
+ self._multi_processor = SynchronousMultiTracingProcessor()
86
+ self._disabled = os.environ.get("OPENAI_AGENTS_DISABLE_TRACING", "false").lower() in (
87
+ "true",
88
+ "1",
89
+ )
90
+
91
+ def register_processor(self, processor: TracingProcessor):
92
+ """
93
+ Add a processor to the list of processors. Each processor will receive all traces/spans.
94
+ """
95
+ self._multi_processor.add_tracing_processor(processor)
96
+
97
+ def set_processors(self, processors: list[TracingProcessor]):
98
+ """
99
+ Set the list of processors. This will replace the current list of processors.
100
+ """
101
+ self._multi_processor.set_processors(processors)
102
+
103
+ def get_current_trace(self) -> Trace | None:
104
+ """
105
+ Returns the currently active trace, if any.
106
+ """
107
+ return Scope.get_current_trace()
108
+
109
+ def get_current_span(self) -> Span[Any] | None:
110
+ """
111
+ Returns the currently active span, if any.
112
+ """
113
+ return Scope.get_current_span()
114
+
115
+ def set_disabled(self, disabled: bool) -> None:
116
+ """
117
+ Set whether tracing is disabled.
118
+ """
119
+ self._disabled = disabled
120
+
121
+ def create_trace(
122
+ self,
123
+ name: str,
124
+ trace_id: str | None = None,
125
+ group_id: str | None = None,
126
+ metadata: dict[str, Any] | None = None,
127
+ disabled: bool = False,
128
+ ) -> Trace:
129
+ """
130
+ Create a new trace.
131
+ """
132
+ if self._disabled or disabled:
133
+ logger.debug(f"Tracing is disabled. Not creating trace {name}")
134
+ return NoOpTrace()
135
+
136
+ trace_id = trace_id or util.gen_trace_id()
137
+
138
+ logger.debug(f"Creating trace {name} with id {trace_id}")
139
+
140
+ return TraceImpl(
141
+ name=name,
142
+ trace_id=trace_id,
143
+ group_id=group_id,
144
+ metadata=metadata,
145
+ processor=self._multi_processor,
146
+ )
147
+
148
+ def create_span(
149
+ self,
150
+ span_data: TSpanData,
151
+ span_id: str | None = None,
152
+ parent: Trace | Span[Any] | None = None,
153
+ disabled: bool = False,
154
+ ) -> Span[TSpanData]:
155
+ """
156
+ Create a new span.
157
+ """
158
+ if self._disabled or disabled:
159
+ logger.debug(f"Tracing is disabled. Not creating span {span_data}")
160
+ return NoOpSpan(span_data)
161
+
162
+ if not parent:
163
+ current_span = Scope.get_current_span()
164
+ current_trace = Scope.get_current_trace()
165
+ if current_trace is None:
166
+ logger.error(
167
+ "No active trace. Make sure to start a trace with `trace()` first"
168
+ "Returning NoOpSpan."
169
+ )
170
+ return NoOpSpan(span_data)
171
+ elif isinstance(current_trace, NoOpTrace) or isinstance(current_span, NoOpSpan):
172
+ logger.debug(
173
+ f"Parent {current_span} or {current_trace} is no-op, returning NoOpSpan"
174
+ )
175
+ return NoOpSpan(span_data)
176
+
177
+ parent_id = current_span.span_id if current_span else None
178
+ trace_id = current_trace.trace_id
179
+
180
+ elif isinstance(parent, Trace):
181
+ if isinstance(parent, NoOpTrace):
182
+ logger.debug(f"Parent {parent} is no-op, returning NoOpSpan")
183
+ return NoOpSpan(span_data)
184
+ trace_id = parent.trace_id
185
+ parent_id = None
186
+ elif isinstance(parent, Span):
187
+ if isinstance(parent, NoOpSpan):
188
+ logger.debug(f"Parent {parent} is no-op, returning NoOpSpan")
189
+ return NoOpSpan(span_data)
190
+ parent_id = parent.span_id
191
+ trace_id = parent.trace_id
192
+
193
+ logger.debug(f"Creating span {span_data} with id {span_id}")
194
+
195
+ return SpanImpl(
196
+ trace_id=trace_id,
197
+ span_id=span_id,
198
+ parent_id=parent_id,
199
+ processor=self._multi_processor,
200
+ span_data=span_data,
201
+ )
202
+
203
+ def shutdown(self) -> None:
204
+ try:
205
+ logger.debug("Shutting down trace provider")
206
+ self._multi_processor.shutdown()
207
+ except Exception as e:
208
+ logger.error(f"Error shutting down trace provider: {e}")
209
+
210
+
211
+ GLOBAL_TRACE_PROVIDER = TraceProvider()
@@ -0,0 +1,188 @@
1
+ from __future__ import annotations
2
+
3
+ import abc
4
+ from collections.abc import Mapping, Sequence
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ if TYPE_CHECKING:
8
+ from openai.types.responses import Response, ResponseInputItemParam
9
+
10
+
11
+ class SpanData(abc.ABC):
12
+ @abc.abstractmethod
13
+ def export(self) -> dict[str, Any]:
14
+ pass
15
+
16
+ @property
17
+ @abc.abstractmethod
18
+ def type(self) -> str:
19
+ pass
20
+
21
+
22
+ class AgentSpanData(SpanData):
23
+ __slots__ = ("name", "handoffs", "tools", "output_type")
24
+
25
+ def __init__(
26
+ self,
27
+ name: str,
28
+ handoffs: list[str] | None = None,
29
+ tools: list[str] | None = None,
30
+ output_type: str | None = None,
31
+ ):
32
+ self.name = name
33
+ self.handoffs: list[str] | None = handoffs
34
+ self.tools: list[str] | None = tools
35
+ self.output_type: str | None = output_type
36
+
37
+ @property
38
+ def type(self) -> str:
39
+ return "agent"
40
+
41
+ def export(self) -> dict[str, Any]:
42
+ return {
43
+ "type": self.type,
44
+ "name": self.name,
45
+ "handoffs": self.handoffs,
46
+ "tools": self.tools,
47
+ "output_type": self.output_type,
48
+ }
49
+
50
+
51
+ class FunctionSpanData(SpanData):
52
+ __slots__ = ("name", "input", "output")
53
+
54
+ def __init__(self, name: str, input: str | None, output: str | None):
55
+ self.name = name
56
+ self.input = input
57
+ self.output = output
58
+
59
+ @property
60
+ def type(self) -> str:
61
+ return "function"
62
+
63
+ def export(self) -> dict[str, Any]:
64
+ return {
65
+ "type": self.type,
66
+ "name": self.name,
67
+ "input": self.input,
68
+ "output": self.output,
69
+ }
70
+
71
+
72
+ class GenerationSpanData(SpanData):
73
+ __slots__ = (
74
+ "input",
75
+ "output",
76
+ "model",
77
+ "model_config",
78
+ "usage",
79
+ )
80
+
81
+ def __init__(
82
+ self,
83
+ input: Sequence[Mapping[str, Any]] | None = None,
84
+ output: Sequence[Mapping[str, Any]] | None = None,
85
+ model: str | None = None,
86
+ model_config: Mapping[str, Any] | None = None,
87
+ usage: dict[str, Any] | None = None,
88
+ ):
89
+ self.input = input
90
+ self.output = output
91
+ self.model = model
92
+ self.model_config = model_config
93
+ self.usage = usage
94
+
95
+ @property
96
+ def type(self) -> str:
97
+ return "generation"
98
+
99
+ def export(self) -> dict[str, Any]:
100
+ return {
101
+ "type": self.type,
102
+ "input": self.input,
103
+ "output": self.output,
104
+ "model": self.model,
105
+ "model_config": self.model_config,
106
+ "usage": self.usage,
107
+ }
108
+
109
+
110
+ class ResponseSpanData(SpanData):
111
+ __slots__ = ("response", "input")
112
+
113
+ def __init__(
114
+ self,
115
+ response: Response | None = None,
116
+ input: str | list[ResponseInputItemParam] | None = None,
117
+ ) -> None:
118
+ self.response = response
119
+ # This is not used by the OpenAI trace processors, but is useful for other tracing
120
+ # processor implementations
121
+ self.input = input
122
+
123
+ @property
124
+ def type(self) -> str:
125
+ return "response"
126
+
127
+ def export(self) -> dict[str, Any]:
128
+ return {
129
+ "type": self.type,
130
+ "response_id": self.response.id if self.response else None,
131
+ }
132
+
133
+
134
+ class HandoffSpanData(SpanData):
135
+ __slots__ = ("from_agent", "to_agent")
136
+
137
+ def __init__(self, from_agent: str | None, to_agent: str | None):
138
+ self.from_agent = from_agent
139
+ self.to_agent = to_agent
140
+
141
+ @property
142
+ def type(self) -> str:
143
+ return "handoff"
144
+
145
+ def export(self) -> dict[str, Any]:
146
+ return {
147
+ "type": self.type,
148
+ "from_agent": self.from_agent,
149
+ "to_agent": self.to_agent,
150
+ }
151
+
152
+
153
+ class CustomSpanData(SpanData):
154
+ __slots__ = ("name", "data")
155
+
156
+ def __init__(self, name: str, data: dict[str, Any]):
157
+ self.name = name
158
+ self.data = data
159
+
160
+ @property
161
+ def type(self) -> str:
162
+ return "custom"
163
+
164
+ def export(self) -> dict[str, Any]:
165
+ return {
166
+ "type": self.type,
167
+ "name": self.name,
168
+ "data": self.data,
169
+ }
170
+
171
+
172
+ class GuardrailSpanData(SpanData):
173
+ __slots__ = ("name", "triggered")
174
+
175
+ def __init__(self, name: str, triggered: bool = False):
176
+ self.name = name
177
+ self.triggered = triggered
178
+
179
+ @property
180
+ def type(self) -> str:
181
+ return "guardrail"
182
+
183
+ def export(self) -> dict[str, Any]:
184
+ return {
185
+ "type": self.type,
186
+ "name": self.name,
187
+ "triggered": self.triggered,
188
+ }
@@ -0,0 +1,264 @@
1
+ from __future__ import annotations
2
+
3
+ import abc
4
+ import contextvars
5
+ from typing import Any, Generic, TypeVar
6
+
7
+ from typing_extensions import TypedDict
8
+
9
+ from . import util
10
+ from .logger import logger
11
+ from .processor_interface import TracingProcessor
12
+ from .scope import Scope
13
+ from .span_data import SpanData
14
+
15
+ TSpanData = TypeVar("TSpanData", bound=SpanData)
16
+
17
+
18
+ class SpanError(TypedDict):
19
+ message: str
20
+ data: dict[str, Any] | None
21
+
22
+
23
+ class Span(abc.ABC, Generic[TSpanData]):
24
+ @property
25
+ @abc.abstractmethod
26
+ def trace_id(self) -> str:
27
+ pass
28
+
29
+ @property
30
+ @abc.abstractmethod
31
+ def span_id(self) -> str:
32
+ pass
33
+
34
+ @property
35
+ @abc.abstractmethod
36
+ def span_data(self) -> TSpanData:
37
+ pass
38
+
39
+ @abc.abstractmethod
40
+ def start(self, mark_as_current: bool = False):
41
+ """
42
+ Start the span.
43
+
44
+ Args:
45
+ mark_as_current: If true, the span will be marked as the current span.
46
+ """
47
+ pass
48
+
49
+ @abc.abstractmethod
50
+ def finish(self, reset_current: bool = False) -> None:
51
+ """
52
+ Finish the span.
53
+
54
+ Args:
55
+ reset_current: If true, the span will be reset as the current span.
56
+ """
57
+ pass
58
+
59
+ @abc.abstractmethod
60
+ def __enter__(self) -> Span[TSpanData]:
61
+ pass
62
+
63
+ @abc.abstractmethod
64
+ def __exit__(self, exc_type, exc_val, exc_tb):
65
+ pass
66
+
67
+ @property
68
+ @abc.abstractmethod
69
+ def parent_id(self) -> str | None:
70
+ pass
71
+
72
+ @abc.abstractmethod
73
+ def set_error(self, error: SpanError) -> None:
74
+ pass
75
+
76
+ @property
77
+ @abc.abstractmethod
78
+ def error(self) -> SpanError | None:
79
+ pass
80
+
81
+ @abc.abstractmethod
82
+ def export(self) -> dict[str, Any] | None:
83
+ pass
84
+
85
+ @property
86
+ @abc.abstractmethod
87
+ def started_at(self) -> str | None:
88
+ pass
89
+
90
+ @property
91
+ @abc.abstractmethod
92
+ def ended_at(self) -> str | None:
93
+ pass
94
+
95
+
96
+ class NoOpSpan(Span[TSpanData]):
97
+ __slots__ = ("_span_data", "_prev_span_token")
98
+
99
+ def __init__(self, span_data: TSpanData):
100
+ self._span_data = span_data
101
+ self._prev_span_token: contextvars.Token[Span[TSpanData] | None] | None = None
102
+
103
+ @property
104
+ def trace_id(self) -> str:
105
+ return "no-op"
106
+
107
+ @property
108
+ def span_id(self) -> str:
109
+ return "no-op"
110
+
111
+ @property
112
+ def span_data(self) -> TSpanData:
113
+ return self._span_data
114
+
115
+ @property
116
+ def parent_id(self) -> str | None:
117
+ return None
118
+
119
+ def start(self, mark_as_current: bool = False):
120
+ if mark_as_current:
121
+ self._prev_span_token = Scope.set_current_span(self)
122
+
123
+ def finish(self, reset_current: bool = False) -> None:
124
+ if reset_current and self._prev_span_token is not None:
125
+ Scope.reset_current_span(self._prev_span_token)
126
+ self._prev_span_token = None
127
+
128
+ def __enter__(self) -> Span[TSpanData]:
129
+ self.start(mark_as_current=True)
130
+ return self
131
+
132
+ def __exit__(self, exc_type, exc_val, exc_tb):
133
+ reset_current = True
134
+ if exc_type is GeneratorExit:
135
+ logger.debug("GeneratorExit, skipping span reset")
136
+ reset_current = False
137
+
138
+ self.finish(reset_current=reset_current)
139
+
140
+ def set_error(self, error: SpanError) -> None:
141
+ pass
142
+
143
+ @property
144
+ def error(self) -> SpanError | None:
145
+ return None
146
+
147
+ def export(self) -> dict[str, Any] | None:
148
+ return None
149
+
150
+ @property
151
+ def started_at(self) -> str | None:
152
+ return None
153
+
154
+ @property
155
+ def ended_at(self) -> str | None:
156
+ return None
157
+
158
+
159
+ class SpanImpl(Span[TSpanData]):
160
+ __slots__ = (
161
+ "_trace_id",
162
+ "_span_id",
163
+ "_parent_id",
164
+ "_started_at",
165
+ "_ended_at",
166
+ "_error",
167
+ "_prev_span_token",
168
+ "_processor",
169
+ "_span_data",
170
+ )
171
+
172
+ def __init__(
173
+ self,
174
+ trace_id: str,
175
+ span_id: str | None,
176
+ parent_id: str | None,
177
+ processor: TracingProcessor,
178
+ span_data: TSpanData,
179
+ ):
180
+ self._trace_id = trace_id
181
+ self._span_id = span_id or util.gen_span_id()
182
+ self._parent_id = parent_id
183
+ self._started_at: str | None = None
184
+ self._ended_at: str | None = None
185
+ self._processor = processor
186
+ self._error: SpanError | None = None
187
+ self._prev_span_token: contextvars.Token[Span[TSpanData] | None] | None = None
188
+ self._span_data = span_data
189
+
190
+ @property
191
+ def trace_id(self) -> str:
192
+ return self._trace_id
193
+
194
+ @property
195
+ def span_id(self) -> str:
196
+ return self._span_id
197
+
198
+ @property
199
+ def span_data(self) -> TSpanData:
200
+ return self._span_data
201
+
202
+ @property
203
+ def parent_id(self) -> str | None:
204
+ return self._parent_id
205
+
206
+ def start(self, mark_as_current: bool = False):
207
+ if self.started_at is not None:
208
+ logger.warning("Span already started")
209
+ return
210
+
211
+ self._started_at = util.time_iso()
212
+ self._processor.on_span_start(self)
213
+ if mark_as_current:
214
+ self._prev_span_token = Scope.set_current_span(self)
215
+
216
+ def finish(self, reset_current: bool = False) -> None:
217
+ if self.ended_at is not None:
218
+ logger.warning("Span already finished")
219
+ return
220
+
221
+ self._ended_at = util.time_iso()
222
+ self._processor.on_span_end(self)
223
+ if reset_current and self._prev_span_token is not None:
224
+ Scope.reset_current_span(self._prev_span_token)
225
+ self._prev_span_token = None
226
+
227
+ def __enter__(self) -> Span[TSpanData]:
228
+ self.start(mark_as_current=True)
229
+ return self
230
+
231
+ def __exit__(self, exc_type, exc_val, exc_tb):
232
+ reset_current = True
233
+ if exc_type is GeneratorExit:
234
+ logger.debug("GeneratorExit, skipping span reset")
235
+ reset_current = False
236
+
237
+ self.finish(reset_current=reset_current)
238
+
239
+ def set_error(self, error: SpanError) -> None:
240
+ self._error = error
241
+
242
+ @property
243
+ def error(self) -> SpanError | None:
244
+ return self._error
245
+
246
+ @property
247
+ def started_at(self) -> str | None:
248
+ return self._started_at
249
+
250
+ @property
251
+ def ended_at(self) -> str | None:
252
+ return self._ended_at
253
+
254
+ def export(self) -> dict[str, Any] | None:
255
+ return {
256
+ "object": "trace.span",
257
+ "id": self.span_id,
258
+ "trace_id": self.trace_id,
259
+ "parent_id": self._parent_id,
260
+ "started_at": self._started_at,
261
+ "ended_at": self._ended_at,
262
+ "span_data": self.span_data.export(),
263
+ "error": self._error,
264
+ }