openai-sdk-helpers 0.4.3__py3-none-any.whl → 0.5.1__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.
Files changed (52) hide show
  1. openai_sdk_helpers/__init__.py +41 -7
  2. openai_sdk_helpers/agent/__init__.py +1 -2
  3. openai_sdk_helpers/agent/base.py +169 -190
  4. openai_sdk_helpers/agent/configuration.py +12 -20
  5. openai_sdk_helpers/agent/coordinator.py +14 -17
  6. openai_sdk_helpers/agent/runner.py +3 -45
  7. openai_sdk_helpers/agent/search/base.py +49 -71
  8. openai_sdk_helpers/agent/search/vector.py +82 -110
  9. openai_sdk_helpers/agent/search/web.py +103 -81
  10. openai_sdk_helpers/agent/summarizer.py +20 -28
  11. openai_sdk_helpers/agent/translator.py +17 -23
  12. openai_sdk_helpers/agent/validator.py +17 -23
  13. openai_sdk_helpers/errors.py +9 -0
  14. openai_sdk_helpers/extract/__init__.py +23 -0
  15. openai_sdk_helpers/extract/extractor.py +157 -0
  16. openai_sdk_helpers/extract/generator.py +476 -0
  17. openai_sdk_helpers/files_api.py +1 -0
  18. openai_sdk_helpers/logging.py +12 -1
  19. openai_sdk_helpers/prompt/extractor_config_agent_instructions.jinja +6 -0
  20. openai_sdk_helpers/prompt/extractor_config_generator.jinja +37 -0
  21. openai_sdk_helpers/prompt/extractor_config_generator_instructions.jinja +9 -0
  22. openai_sdk_helpers/prompt/extractor_prompt_optimizer_agent_instructions.jinja +4 -0
  23. openai_sdk_helpers/prompt/extractor_prompt_optimizer_request.jinja +11 -0
  24. openai_sdk_helpers/response/__init__.py +2 -6
  25. openai_sdk_helpers/response/base.py +233 -164
  26. openai_sdk_helpers/response/configuration.py +39 -14
  27. openai_sdk_helpers/response/files.py +41 -2
  28. openai_sdk_helpers/response/runner.py +1 -48
  29. openai_sdk_helpers/response/tool_call.py +0 -141
  30. openai_sdk_helpers/response/vector_store.py +8 -5
  31. openai_sdk_helpers/streamlit_app/app.py +1 -9
  32. openai_sdk_helpers/structure/__init__.py +16 -0
  33. openai_sdk_helpers/structure/base.py +239 -278
  34. openai_sdk_helpers/structure/extraction.py +1228 -0
  35. openai_sdk_helpers/structure/plan/plan.py +0 -20
  36. openai_sdk_helpers/structure/plan/task.py +0 -33
  37. openai_sdk_helpers/structure/prompt.py +16 -0
  38. openai_sdk_helpers/structure/responses.py +2 -2
  39. openai_sdk_helpers/structure/web_search.py +0 -10
  40. openai_sdk_helpers/tools.py +346 -99
  41. openai_sdk_helpers/utils/__init__.py +7 -0
  42. openai_sdk_helpers/utils/json/base_model.py +315 -32
  43. openai_sdk_helpers/utils/langextract.py +194 -0
  44. openai_sdk_helpers/vector_storage/cleanup.py +7 -2
  45. openai_sdk_helpers/vector_storage/storage.py +37 -7
  46. {openai_sdk_helpers-0.4.3.dist-info → openai_sdk_helpers-0.5.1.dist-info}/METADATA +21 -6
  47. openai_sdk_helpers-0.5.1.dist-info/RECORD +95 -0
  48. openai_sdk_helpers/streamlit_app/streamlit_web_search.py +0 -75
  49. openai_sdk_helpers-0.4.3.dist-info/RECORD +0 -86
  50. {openai_sdk_helpers-0.4.3.dist-info → openai_sdk_helpers-0.5.1.dist-info}/WHEEL +0 -0
  51. {openai_sdk_helpers-0.4.3.dist-info → openai_sdk_helpers-0.5.1.dist-info}/entry_points.txt +0 -0
  52. {openai_sdk_helpers-0.4.3.dist-info → openai_sdk_helpers-0.5.1.dist-info}/licenses/LICENSE +0 -0
@@ -2,41 +2,37 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import json
6
- from pathlib import Path
7
- from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Protocol, cast
5
+ import logging
6
+ import traceback
8
7
  import uuid
8
+ from pathlib import Path
9
+ from typing import TYPE_CHECKING, Any, Dict, Optional, Protocol, cast
9
10
 
10
- from agents import (
11
- Agent,
12
- Handoff,
13
- InputGuardrail,
14
- OutputGuardrail,
15
- RunResultStreaming,
16
- Session,
17
- )
11
+ from agents import Agent, Handoff, InputGuardrail, OutputGuardrail, Session
18
12
  from agents.model_settings import ModelSettings
19
13
  from agents.run_context import RunContextWrapper
20
14
  from agents.tool import Tool
21
15
  from jinja2 import Template
22
16
 
17
+ from ..environment import get_data_path
23
18
  from ..utils.json.data_class import DataclassJSONSerializable
24
19
  from ..structure.base import StructureBase
25
- from ..structure.prompt import PromptStructure
26
-
20
+ from ..tools import (
21
+ StructureType,
22
+ ToolHandlerRegistration,
23
+ ToolSpec,
24
+ )
27
25
 
28
26
  from ..utils import (
29
27
  check_filepath,
30
28
  log,
31
29
  )
32
30
 
33
- from ..tools import tool_handler_factory
34
-
35
- from .runner import run_async, run_streamed, run_sync
31
+ from .runner import run_async, run_sync
36
32
 
37
33
  if TYPE_CHECKING:
38
34
  from ..settings import OpenAISettings
39
- from ..response.base import ResponseBase, ToolHandler
35
+ from ..response.base import ResponseBase
40
36
 
41
37
 
42
38
  class AgentConfigurationProtocol(Protocol):
@@ -52,18 +48,14 @@ class AgentConfigurationProtocol(Protocol):
52
48
  """Agent description."""
53
49
  ...
54
50
 
55
- @property
56
- def model(self) -> Optional[str]:
57
- """Model identifier."""
58
- ...
59
-
60
51
  @property
61
52
  def template_path(self) -> Optional[str | Path]:
62
53
  """Template path."""
63
54
  ...
64
55
 
65
- def resolve_prompt_path(self, prompt_dir: Path | None = None) -> Path | None:
66
- """Resolve the prompt template path."""
56
+ @property
57
+ def model(self) -> Optional[str]:
58
+ """Model identifier."""
67
59
  ...
68
60
 
69
61
  @property
@@ -136,7 +128,7 @@ class AgentBase(DataclassJSONSerializable):
136
128
  ... description="A custom agent",
137
129
  ... model="gpt-4o-mini"
138
130
  ... )
139
- >>> agent = AgentBase(configuration=configuration, default_model="gpt-4o-mini")
131
+ >>> agent = AgentBase(configuration=configuration)
140
132
  >>> result = agent.run_sync("What is 2+2?")
141
133
 
142
134
  Use absolute path to template:
@@ -146,7 +138,7 @@ class AgentBase(DataclassJSONSerializable):
146
138
  ... template_path="/absolute/path/to/template.jinja",
147
139
  ... model="gpt-4o-mini"
148
140
  ... )
149
- >>> agent = AgentBase(configuration=configuration, default_model="gpt-4o-mini")
141
+ >>> agent = AgentBase(configuration=configuration)
150
142
 
151
143
  Use async execution:
152
144
 
@@ -186,14 +178,14 @@ class AgentBase(DataclassJSONSerializable):
186
178
  Execute the agent asynchronously and optionally cast the result.
187
179
  run_sync(input, context, output_structure, session)
188
180
  Execute the agent synchronously.
189
- run_streamed(input, context, output_structure, session)
190
- Return a streaming result for the agent execution.
191
181
  as_tool()
192
182
  Return the agent as a callable tool.
193
183
  as_response_tool()
194
184
  Return response tool handler and definition for Responses API use.
195
185
  build_response(openai_settings, data_path=None, tool_handlers=None, system_vector_store=None)
196
186
  Build a ResponseBase instance based on this agent.
187
+ save_error(exc)
188
+ Persist error details to a file named with the agent UUID.
197
189
  close()
198
190
  Clean up agent resources (can be overridden by subclasses).
199
191
  """
@@ -204,8 +196,6 @@ class AgentBase(DataclassJSONSerializable):
204
196
  configuration: AgentConfigurationProtocol,
205
197
  run_context_wrapper: Optional[RunContextWrapper[Dict[str, Any]]] = None,
206
198
  data_path: Path | str | None = None,
207
- prompt_dir: Optional[Path] = None,
208
- default_model: Optional[str] = None,
209
199
  ) -> None:
210
200
  """Initialize the AgentBase using a configuration object.
211
201
 
@@ -215,39 +205,31 @@ class AgentBase(DataclassJSONSerializable):
215
205
  Configuration describing this agent.
216
206
  run_context_wrapper : RunContextWrapper or None, default=None
217
207
  Optional wrapper providing runtime context for prompt rendering.
218
- prompt_dir : Path or None, default=None
219
- Optional directory holding prompt templates. Used when
220
- ``configuration.template_path`` is not provided or is relative. If
221
- ``configuration.template_path`` is an absolute path, this parameter is
222
- ignored.
223
- default_model : str or None, default=None
224
- Optional fallback model identifier if the configuration does not supply one.
208
+ data_path : Path | str | None, default=None
209
+ Optional base path for storing agent data.
225
210
  """
226
- name = configuration.name
227
- description = configuration.description or ""
228
- model = configuration.model or default_model
229
- if not model:
230
- raise ValueError("Model is required to construct the agent.")
231
-
232
- prompt_path = configuration.resolve_prompt_path(prompt_dir)
211
+ self._configuration = configuration
212
+ self.uuid = uuid.uuid4()
213
+ self._model = configuration.model
214
+ if self._model is None:
215
+ raise ValueError(
216
+ f"Model must be specified in configuration for agent '{configuration.name}'."
217
+ )
233
218
 
234
219
  # Build template from file or fall back to instructions
235
- if prompt_path is None:
220
+ self._template_path = configuration.template_path
221
+ if self._template_path is None:
236
222
  instructions_text = configuration.instructions_text
237
223
  self._template = Template(instructions_text)
238
224
  self._instructions = instructions_text
239
- elif prompt_path.exists():
240
- self._template = Template(prompt_path.read_text(encoding="utf-8"))
241
- self._instructions = None
242
225
  else:
243
- raise FileNotFoundError(
244
- f"Prompt template for agent '{name}' not found at {prompt_path}."
245
- )
246
-
247
- self._name = name
248
- self.uuid = uuid.uuid4()
249
- self.description = description
250
- self.model = model
226
+ self._template_path = Path(self._template_path)
227
+ if not self._template_path.exists():
228
+ raise FileNotFoundError(
229
+ f"Template for agent '{self._configuration.name}' not found at {self._template_path}."
230
+ )
231
+ self._template = Template(self._template_path.read_text(encoding="utf-8"))
232
+ self._instructions = None
251
233
 
252
234
  # Resolve data_path with class name appended
253
235
  class_name = self.__class__.__name__
@@ -258,8 +240,6 @@ class AgentBase(DataclassJSONSerializable):
258
240
  else:
259
241
  self._data_path = data_path_obj / class_name
260
242
  else:
261
- from ..environment import get_data_path
262
-
263
243
  self._data_path = get_data_path(self.__class__.__name__)
264
244
 
265
245
  self._input_structure = configuration.input_structure
@@ -337,7 +317,29 @@ class AgentBase(DataclassJSONSerializable):
337
317
  str
338
318
  Name used to identify the agent.
339
319
  """
340
- return self._name
320
+ return self._configuration.name
321
+
322
+ @property
323
+ def description(self) -> Optional[str]:
324
+ """Return the description of this agent.
325
+
326
+ Returns
327
+ -------
328
+ str or None
329
+ Description of the agent's purpose.
330
+ """
331
+ return self._configuration.description
332
+
333
+ @property
334
+ def model(self) -> str:
335
+ """Return the model identifier for this agent.
336
+
337
+ Returns
338
+ -------
339
+ str
340
+ Model identifier used by the agent.
341
+ """
342
+ return self._model # pyright: ignore[reportReturnType]
341
343
 
342
344
  @property
343
345
  def instructions_text(self) -> str:
@@ -348,9 +350,7 @@ class AgentBase(DataclassJSONSerializable):
348
350
  str
349
351
  Rendered instructions text using the current run context.
350
352
  """
351
- if self._instructions is not None:
352
- return self._instructions
353
- return self._build_prompt_from_jinja()
353
+ return self._configuration.instructions_text
354
354
 
355
355
  @property
356
356
  def tools(self) -> Optional[list]:
@@ -361,7 +361,7 @@ class AgentBase(DataclassJSONSerializable):
361
361
  list or None
362
362
  Tool definitions configured for the agent.
363
363
  """
364
- return self._tools
364
+ return self._configuration.tools
365
365
 
366
366
  @property
367
367
  def output_structure(self) -> Optional[type[StructureBase]]:
@@ -372,7 +372,7 @@ class AgentBase(DataclassJSONSerializable):
372
372
  type[StructureBase] or None
373
373
  Output type used to cast responses.
374
374
  """
375
- return self._output_structure
375
+ return self._configuration.output_structure
376
376
 
377
377
  @property
378
378
  def model_settings(self) -> Optional[ModelSettings]:
@@ -438,14 +438,14 @@ class AgentBase(DataclassJSONSerializable):
438
438
  Initialized agent ready for execution.
439
439
  """
440
440
  agent_config: Dict[str, Any] = {
441
- "name": self._name,
442
- "instructions": self._build_prompt_from_jinja() or ".",
443
- "model": self.model,
441
+ "name": self._configuration.name,
442
+ "instructions": self._configuration.instructions_text or ".",
443
+ "model": self._model,
444
444
  }
445
- if self._output_structure:
446
- agent_config["output_type"] = self._output_structure
447
- if self._tools:
448
- agent_config["tools"] = self._tools
445
+ if self._configuration.output_structure:
446
+ agent_config["output_type"] = self._configuration.output_structure
447
+ if self._configuration.tools:
448
+ agent_config["tools"] = self._configuration.tools
449
449
  if self._model_settings:
450
450
  agent_config["model_settings"] = self._model_settings
451
451
  if self._handoffs:
@@ -488,13 +488,29 @@ class AgentBase(DataclassJSONSerializable):
488
488
  output_structure = self._output_structure
489
489
  # Use session from parameter, fall back to configuration session
490
490
  session_to_use = session if session is not None else self._session
491
- return await run_async(
492
- agent=self.get_agent(),
493
- input=input,
494
- context=context,
495
- output_structure=output_structure,
496
- session=session_to_use,
497
- )
491
+ try:
492
+ return await run_async(
493
+ agent=self.get_agent(),
494
+ input=input,
495
+ context=context,
496
+ output_structure=output_structure,
497
+ session=session_to_use,
498
+ )
499
+ except Exception as exc:
500
+ try:
501
+ self.save_error(exc)
502
+ except Exception as save_exc:
503
+ log(
504
+ f"Failed to save error details for agent {self.uuid}: {save_exc}",
505
+ level=logging.ERROR,
506
+ exc=save_exc,
507
+ )
508
+ log(
509
+ f"Error running agent '{self.name}': {exc}",
510
+ level=logging.ERROR,
511
+ exc=exc,
512
+ )
513
+ raise
498
514
 
499
515
  def run_sync(
500
516
  self,
@@ -527,54 +543,29 @@ class AgentBase(DataclassJSONSerializable):
527
543
  output_structure = self._output_structure
528
544
  # Use session from parameter, fall back to configuration session
529
545
  session_to_use = session if session is not None else self._session
530
- return run_sync(
531
- agent=self.get_agent(),
532
- input=input,
533
- context=context,
534
- output_structure=output_structure,
535
- session=session_to_use,
536
- )
537
-
538
- def run_streamed(
539
- self,
540
- input: str,
541
- *,
542
- context: Optional[Dict[str, Any]] = None,
543
- output_structure: Optional[type[StructureBase]] = None,
544
- session: Optional[Any] = None,
545
- ) -> RunResultStreaming | StructureBase:
546
- """Stream the agent execution results.
547
-
548
- Parameters
549
- ----------
550
- input : str
551
- Prompt or query for the agent.
552
- context : dict or None, default=None
553
- Optional dictionary passed to the agent.
554
- output_structure : type[StructureBase] or None, default=None
555
- Optional type used to cast the final output.
556
- session : Session or None, default=None
557
- Optional session for maintaining conversation history across runs.
558
- If not provided, uses the session from configuration if available.
559
-
560
- Returns
561
- -------
562
- RunResultStreaming
563
- Streaming output wrapper from the agent execution.
564
- """
565
- # Use session from parameter, fall back to configuration session
566
- session_to_use = session if session is not None else self._session
567
- output_structure_to_use = output_structure or self._output_structure
568
- result = run_streamed(
569
- agent=self.get_agent(),
570
- input=input,
571
- context=context,
572
- output_structure=output_structure_to_use,
573
- session=session_to_use,
574
- )
575
- if output_structure_to_use and hasattr(result, "final_output_as"):
576
- return cast(Any, result).final_output_as(output_structure_to_use)
577
- return result
546
+ try:
547
+ return run_sync(
548
+ agent=self.get_agent(),
549
+ input=input,
550
+ context=context,
551
+ output_structure=output_structure,
552
+ session=session_to_use,
553
+ )
554
+ except Exception as exc:
555
+ try:
556
+ self.save_error(exc)
557
+ except Exception as save_exc:
558
+ log(
559
+ f"Failed to save error details for agent {self.uuid}: {save_exc}",
560
+ level=logging.ERROR,
561
+ exc=save_exc,
562
+ )
563
+ log(
564
+ f"Error running agent '{self.name}': {exc}",
565
+ level=logging.ERROR,
566
+ exc=exc,
567
+ )
568
+ raise
578
569
 
579
570
  def as_tool(self) -> Tool:
580
571
  """Return the agent as a callable tool.
@@ -586,78 +577,35 @@ class AgentBase(DataclassJSONSerializable):
586
577
  """
587
578
  agent = self.get_agent()
588
579
  tool_obj: Tool = agent.as_tool(
589
- tool_name=self._name, tool_description=self.description
580
+ tool_name=self._configuration.name,
581
+ tool_description=self._configuration.description,
590
582
  )
591
583
  return tool_obj
592
584
 
593
- def as_response_tool(
585
+ def as_tool_handler_registration(
594
586
  self,
595
- *,
596
- tool_name: str | None = None,
597
- tool_description: str | None = None,
598
- ) -> tuple[dict[str, Callable[..., Any]], dict[str, Any]]:
599
- """Return response tool handler and definition for Responses API use.
600
-
601
- The returned handler serializes tool output as JSON using
602
- ``tool_handler_factory`` so downstream response flows can rely on a
603
- consistent payload format.
587
+ ) -> ToolHandlerRegistration:
588
+ """Return the agent as a ToolHandlerRegistration for Responses API use.
604
589
 
605
590
  Parameters
606
591
  ----------
607
592
  tool_name : str or None, default=None
608
593
  Optional override for the tool name. When None, uses the agent name.
609
- tool_description : str or None, default=None
610
- Optional override for the tool description. When None, uses the
611
- agent description.
612
-
613
- Returns
614
- -------
615
- tuple[dict[str, Callable[..., Any]], dict[str, Any]]
616
- Tool handler mapping and tool definition for Responses API usage.
617
-
618
- Examples
619
- --------
620
- >>> tool_handler, tool_definition = agent.as_response_tool()
621
- >>> response = ResponseBase(
622
- ... name="agent_tool",
623
- ... instructions="Use the agent tool when needed.",
624
- ... tools=[tool_definition],
625
- ... output_structure=None,
626
- ... tool_handlers=tool_handler,
627
- ... openai_settings=settings,
628
- ... )
629
- >>> response.run_sync("Invoke the agent tool") # doctest: +SKIP
630
594
  """
631
-
632
- def _run_agent(**kwargs: Any) -> Any:
633
- prompt = kwargs.get("prompt")
634
- if prompt is None:
635
- if len(kwargs) == 1:
636
- prompt = next(iter(kwargs.values()))
637
- else:
638
- prompt = json.dumps(kwargs)
639
- return self.run_sync(str(prompt))
640
-
641
- name = tool_name or self.name
642
- description = tool_description or self.description
643
- input_model = self._input_structure or PromptStructure
644
- tool_handler = {name: tool_handler_factory(_run_agent, input_model=input_model)}
645
- tool_definition = {
646
- "type": "function",
647
- "name": name,
648
- "description": description,
649
- "strict": True,
650
- "additionalProperties": False,
651
- "parameters": self._build_response_parameters(),
652
- }
653
- return tool_handler, tool_definition
595
+ tool_spec = ToolSpec(
596
+ tool_name=self.name,
597
+ tool_description=self.description,
598
+ input_structure=cast(StructureType, self._configuration.input_structure),
599
+ output_structure=cast(StructureType, self._configuration.output_structure),
600
+ )
601
+ return ToolHandlerRegistration(handler=self.run_sync, tool_spec=tool_spec)
654
602
 
655
603
  def build_response(
656
604
  self,
657
605
  *,
658
606
  openai_settings: OpenAISettings,
659
607
  data_path: Path | str | None = None,
660
- tool_handlers: dict[str, ToolHandler] | None = None,
608
+ tool_handlers: dict[str, ToolHandlerRegistration] | None = None,
661
609
  system_vector_store: list[str] | None = None,
662
610
  ) -> ResponseBase[StructureBase]:
663
611
  """Build a ResponseBase instance from this agent configuration.
@@ -669,8 +617,9 @@ class AgentBase(DataclassJSONSerializable):
669
617
  data_path : Path, str, or None, default None
670
618
  Optional path for storing response artifacts. When None, the
671
619
  response uses the default data directory.
672
- tool_handlers : dict[str, ToolHandler] or None, default None
673
- Optional mapping of tool names to handler callables.
620
+ tool_handlers : dict[str, ToolHandlerRegistration] or None, default None
621
+ Optional mapping of tool names to handler registrations. Registrations
622
+ can include ToolSpec metadata to parse tool outputs by name.
674
623
  system_vector_store : list[str] or None, default None
675
624
  Optional list of vector store names to attach as system context.
676
625
 
@@ -684,7 +633,7 @@ class AgentBase(DataclassJSONSerializable):
684
633
  >>> from openai_sdk_helpers import OpenAISettings
685
634
  >>> response = agent.build_response(openai_settings=OpenAISettings.from_env())
686
635
  """
687
- from ..response.base import ResponseBase, ToolHandler
636
+ from ..response.base import ResponseBase
688
637
  from ..settings import OpenAISettings
689
638
 
690
639
  if not isinstance(openai_settings, OpenAISettings):
@@ -693,8 +642,8 @@ class AgentBase(DataclassJSONSerializable):
693
642
  tools = self._normalize_response_tools(self.tools)
694
643
 
695
644
  return ResponseBase(
696
- name=self.name,
697
- instructions=self.instructions_text,
645
+ name=self._configuration.name,
646
+ instructions=self._configuration.instructions_text,
698
647
  tools=tools,
699
648
  output_structure=self.output_structure,
700
649
  system_vector_store=system_vector_store,
@@ -773,7 +722,7 @@ class AgentBase(DataclassJSONSerializable):
773
722
 
774
723
  Examples
775
724
  --------
776
- >>> agent = AgentBase(configuration, default_model="gpt-4o-mini")
725
+ >>> agent = AgentBase(configuration)
777
726
  >>> try:
778
727
  ... result = agent.run_sync("query")
779
728
  ... finally:
@@ -790,7 +739,7 @@ class AgentBase(DataclassJSONSerializable):
790
739
  str
791
740
  String representation including agent name and model.
792
741
  """
793
- return f"<AgentBase name={self._name!r} model={self.model!r}>"
742
+ return f"<AgentBase name={self.name!r} model={self.model!r}>"
794
743
 
795
744
  def save(self, filepath: str | Path | None = None) -> None:
796
745
  """Serialize the message history to a JSON file.
@@ -809,11 +758,41 @@ class AgentBase(DataclassJSONSerializable):
809
758
  target = Path(filepath)
810
759
  else:
811
760
  filename = f"{str(self.uuid).lower()}.json"
812
- target = self._data_path / self._name / filename
761
+ target = self._data_path / self.name / filename
813
762
 
814
763
  checked = check_filepath(filepath=target)
815
764
  self.to_json_file(filepath=checked)
816
765
  log(f"Saved messages to {target}")
817
766
 
767
+ def save_error(self, exc: BaseException) -> Path:
768
+ """Persist error details to a file named with the agent UUID.
769
+
770
+ Parameters
771
+ ----------
772
+ exc : BaseException
773
+ Exception instance to serialize.
774
+
775
+ Returns
776
+ -------
777
+ Path
778
+ Path to the error file written to disk.
779
+
780
+ Examples
781
+ --------
782
+ >>> try:
783
+ ... agent.run_sync("trigger error")
784
+ ... except Exception as exc:
785
+ ... agent.save_error(exc)
786
+ """
787
+ error_text = "".join(
788
+ traceback.format_exception(type(exc), exc, exc.__traceback__)
789
+ )
790
+ filename = f"{str(self.uuid).lower()}_error.txt"
791
+ target = self._data_path / self.name / filename
792
+ checked = check_filepath(filepath=target)
793
+ checked.write_text(error_text, encoding="utf-8")
794
+ log(f"Saved error details to {checked}")
795
+ return checked
796
+
818
797
 
819
798
  __all__ = ["AgentConfigurationProtocol", "AgentBase"]
@@ -150,7 +150,7 @@ class AgentConfiguration(DataclassJSONSerializable):
150
150
  Return the resolved instruction content as a string.
151
151
  resolve_prompt_path(prompt_dir)
152
152
  Resolve the prompt template path for this configuration.
153
- gen_agent(run_context_wrapper, prompt_dir, default_model)
153
+ gen_agent(run_context_wrapper)
154
154
  Create a AgentBase instance from this configuration.
155
155
  replace(**changes)
156
156
  Create a new AgentConfiguration with specified fields replaced.
@@ -176,17 +176,17 @@ class AgentConfiguration(DataclassJSONSerializable):
176
176
 
177
177
  name: str
178
178
  instructions: str | Path
179
- description: Optional[str] = None
180
- model: Optional[str] = None
181
- template_path: Optional[str | Path] = None
182
- input_structure: Optional[Type[StructureBase]] = None
183
- output_structure: Optional[Type[StructureBase]] = None
184
- tools: Optional[list] = None
185
- model_settings: Optional[ModelSettings] = None
186
- handoffs: Optional[list[Agent | Handoff]] = None
187
- input_guardrails: Optional[list[InputGuardrail]] = None
188
- output_guardrails: Optional[list[OutputGuardrail]] = None
189
- session: Optional[Session] = None
179
+ description: str | None = None
180
+ model: str | None = None
181
+ template_path: str | Path | None = None
182
+ input_structure: type[StructureBase] | None = None
183
+ output_structure: type[StructureBase] | None = None
184
+ tools: list | None = None
185
+ model_settings: ModelSettings | None = None
186
+ handoffs: list[Agent | Handoff] | None = None
187
+ input_guardrails: list[InputGuardrail] | None = None
188
+ output_guardrails: list[OutputGuardrail] | None = None
189
+ session: Session | None = None
190
190
  add_output_instructions: bool = False
191
191
  add_web_search_tool: bool = False
192
192
 
@@ -294,8 +294,6 @@ class AgentConfiguration(DataclassJSONSerializable):
294
294
  def gen_agent(
295
295
  self,
296
296
  run_context_wrapper: Any = None,
297
- prompt_dir: Path | None = None,
298
- default_model: str | None = None,
299
297
  ) -> Any:
300
298
  """Create a AgentBase instance from this configuration.
301
299
 
@@ -305,10 +303,6 @@ class AgentConfiguration(DataclassJSONSerializable):
305
303
  ----------
306
304
  run_context_wrapper : RunContextWrapper or None, default=None
307
305
  Optional wrapper providing runtime context for prompt rendering.
308
- prompt_dir : Path or None, default=None
309
- Optional directory holding prompt templates.
310
- default_model : str or None, default=None
311
- Optional fallback model identifier if configuration doesn't specify one.
312
306
 
313
307
  Returns
314
308
  -------
@@ -329,8 +323,6 @@ class AgentConfiguration(DataclassJSONSerializable):
329
323
  return AgentBase(
330
324
  configuration=self,
331
325
  run_context_wrapper=run_context_wrapper,
332
- prompt_dir=prompt_dir,
333
- default_model=default_model,
334
326
  )
335
327
 
336
328
  def replace(self, **changes: Any) -> AgentConfiguration: