openai-sdk-helpers 0.2.0__py3-none-any.whl → 0.4.0__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 (58) hide show
  1. openai_sdk_helpers/__init__.py +6 -6
  2. openai_sdk_helpers/agent/__init__.py +4 -2
  3. openai_sdk_helpers/agent/base.py +391 -106
  4. openai_sdk_helpers/agent/config.py +405 -44
  5. openai_sdk_helpers/agent/coordination.py +68 -31
  6. openai_sdk_helpers/agent/runner.py +29 -19
  7. openai_sdk_helpers/agent/search/base.py +103 -54
  8. openai_sdk_helpers/agent/search/vector.py +99 -68
  9. openai_sdk_helpers/agent/search/web.py +84 -50
  10. openai_sdk_helpers/agent/summarizer.py +33 -7
  11. openai_sdk_helpers/agent/translator.py +58 -24
  12. openai_sdk_helpers/agent/validation.py +35 -4
  13. openai_sdk_helpers/cli.py +42 -0
  14. openai_sdk_helpers/config.py +0 -1
  15. openai_sdk_helpers/environment.py +3 -2
  16. openai_sdk_helpers/files_api.py +35 -3
  17. openai_sdk_helpers/prompt/base.py +6 -0
  18. openai_sdk_helpers/response/__init__.py +3 -3
  19. openai_sdk_helpers/response/base.py +161 -22
  20. openai_sdk_helpers/response/config.py +50 -200
  21. openai_sdk_helpers/response/files.py +5 -5
  22. openai_sdk_helpers/response/messages.py +3 -3
  23. openai_sdk_helpers/response/runner.py +7 -7
  24. openai_sdk_helpers/response/tool_call.py +94 -4
  25. openai_sdk_helpers/response/vector_store.py +3 -3
  26. openai_sdk_helpers/streamlit_app/app.py +16 -16
  27. openai_sdk_helpers/streamlit_app/config.py +38 -37
  28. openai_sdk_helpers/streamlit_app/streamlit_web_search.py +2 -2
  29. openai_sdk_helpers/structure/__init__.py +6 -2
  30. openai_sdk_helpers/structure/agent_blueprint.py +2 -2
  31. openai_sdk_helpers/structure/base.py +8 -99
  32. openai_sdk_helpers/structure/plan/plan.py +2 -2
  33. openai_sdk_helpers/structure/plan/task.py +9 -9
  34. openai_sdk_helpers/structure/prompt.py +2 -2
  35. openai_sdk_helpers/structure/responses.py +15 -15
  36. openai_sdk_helpers/structure/summary.py +3 -3
  37. openai_sdk_helpers/structure/translation.py +32 -0
  38. openai_sdk_helpers/structure/validation.py +2 -2
  39. openai_sdk_helpers/structure/vector_search.py +7 -7
  40. openai_sdk_helpers/structure/web_search.py +6 -6
  41. openai_sdk_helpers/tools.py +41 -15
  42. openai_sdk_helpers/utils/__init__.py +19 -5
  43. openai_sdk_helpers/utils/instructions.py +35 -0
  44. openai_sdk_helpers/utils/json/__init__.py +55 -0
  45. openai_sdk_helpers/utils/json/base_model.py +181 -0
  46. openai_sdk_helpers/utils/{json_utils.py → json/data_class.py} +43 -70
  47. openai_sdk_helpers/utils/json/ref.py +113 -0
  48. openai_sdk_helpers/utils/json/utils.py +203 -0
  49. openai_sdk_helpers/utils/output_validation.py +21 -1
  50. openai_sdk_helpers/utils/path_utils.py +34 -1
  51. openai_sdk_helpers/utils/registry.py +194 -0
  52. openai_sdk_helpers/vector_storage/storage.py +10 -0
  53. {openai_sdk_helpers-0.2.0.dist-info → openai_sdk_helpers-0.4.0.dist-info}/METADATA +7 -7
  54. openai_sdk_helpers-0.4.0.dist-info/RECORD +86 -0
  55. openai_sdk_helpers-0.2.0.dist-info/RECORD +0 -79
  56. {openai_sdk_helpers-0.2.0.dist-info → openai_sdk_helpers-0.4.0.dist-info}/WHEEL +0 -0
  57. {openai_sdk_helpers-0.2.0.dist-info → openai_sdk_helpers-0.4.0.dist-info}/entry_points.txt +0 -0
  58. {openai_sdk_helpers-0.2.0.dist-info → openai_sdk_helpers-0.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -2,67 +2,428 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Any, List, Optional, Type
5
+ from dataclasses import dataclass, field
6
+ from pathlib import Path
7
+ from typing import Any, Optional, Type
6
8
 
9
+ from agents import Agent, Handoff, InputGuardrail, OutputGuardrail, Session
7
10
  from agents.model_settings import ModelSettings
8
- from pydantic import BaseModel, ConfigDict, Field
9
11
 
10
- from ..structure import BaseStructure
12
+ from ..utils.json.data_class import DataclassJSONSerializable
13
+ from ..utils.registry import BaseRegistry
14
+ from ..utils.instructions import resolve_instructions_from_path
15
+ from ..structure.base import StructureBase
11
16
 
12
17
 
13
- class AgentConfig(BaseStructure):
14
- """Configuration required to build an AgentBase.
18
+ class AgentConfigurationRegistry(BaseRegistry["AgentConfiguration"]):
19
+ """Registry for managing AgentConfiguration instances.
15
20
 
16
- Methods
21
+ Inherits from BaseRegistry to provide centralized storage and retrieval
22
+ of agent configurations, enabling reusable agent specs across the application.
23
+
24
+ Examples
25
+ --------
26
+ >>> registry = AgentConfigurationRegistry()
27
+ >>> config = AgentConfiguration(
28
+ ... name="test_agent",
29
+ ... model="gpt-4o-mini",
30
+ ... instructions="Test instructions"
31
+ ... )
32
+ >>> registry.register(config)
33
+ >>> retrieved = registry.get("test_agent")
34
+ >>> retrieved.name
35
+ 'test_agent'
36
+ """
37
+
38
+ def load_from_directory(
39
+ self,
40
+ path: Path | str,
41
+ *,
42
+ config_class: type["AgentConfiguration"] | None = None,
43
+ ) -> int:
44
+ """Load all agent configurations from JSON files in a directory.
45
+
46
+ Parameters
47
+ ----------
48
+ path : Path or str
49
+ Directory path containing JSON configuration files.
50
+ config_class : type[AgentConfiguration], optional
51
+ The configuration class to use for deserialization.
52
+ Defaults to AgentConfiguration.
53
+
54
+ Returns
55
+ -------
56
+ int
57
+ Number of configurations successfully loaded and registered.
58
+
59
+ Raises
60
+ ------
61
+ FileNotFoundError
62
+ If the directory does not exist.
63
+ NotADirectoryError
64
+ If the path is not a directory.
65
+
66
+ Examples
67
+ --------
68
+ >>> registry = AgentConfigurationRegistry()
69
+ >>> count = registry.load_from_directory("./agents")
70
+ >>> print(f"Loaded {count} configurations")
71
+ """
72
+ if config_class is None:
73
+ config_class = AgentConfiguration
74
+ return super().load_from_directory(path, config_class=config_class)
75
+
76
+
77
+ def get_default_registry() -> AgentConfigurationRegistry:
78
+ """Return the global default registry instance.
79
+
80
+ Returns
17
81
  -------
18
- print()
19
- Return a human-readable representation of the configuration.
82
+ AgentConfigurationRegistry
83
+ Singleton registry for application-wide configuration storage.
84
+
85
+ Examples
86
+ --------
87
+ >>> registry = get_default_registry()
88
+ >>> config = AgentConfiguration(
89
+ ... name="test", model="gpt-4o-mini", instructions="Test instructions"
90
+ ... )
91
+ >>> registry.register(config)
20
92
  """
93
+ return _default_registry
21
94
 
22
- model_config = ConfigDict(arbitrary_types_allowed=True)
23
95
 
24
- name: str = Field(title="Agent Name", description="Unique name for the agent")
25
- description: Optional[str] = Field(
26
- default=None, title="Description", description="Short description of the agent"
27
- )
28
- model: Optional[str] = Field(
29
- default=None, title="Model", description="Model identifier to use"
30
- )
31
- template_path: Optional[str] = Field(
32
- default=None,
33
- title="Template Path",
34
- description="Path to the Jinja template (absolute or relative to prompt_dir)",
35
- )
36
- input_type: Optional[Type[BaseModel]] = Field(
37
- default=None,
38
- title="Input Type",
39
- description="Pydantic model describing the agent input",
40
- )
41
- output_type: Optional[Type[Any]] = Field(
42
- default=None,
43
- title="Output Type",
44
- description="Type describing the agent output; commonly a Pydantic model or builtin like ``str``",
45
- )
46
- tools: Optional[List[Any]] = Field(
47
- default=None,
48
- title="Tools",
49
- description="Tools available to the agent",
50
- )
51
- model_settings: Optional[ModelSettings] = Field(
52
- default=None, title="Model Settings", description="Additional model settings"
96
+ @dataclass(frozen=True, slots=True)
97
+ class AgentConfiguration(DataclassJSONSerializable):
98
+ """Immutable configuration for building a AgentBase.
99
+
100
+ Encapsulates all metadata required to define an agent including its
101
+ instructions, tools, model settings, handoffs, guardrails, and session
102
+ management. Inherits from DataclassJSONSerializable to support serialization.
103
+
104
+ This dataclass is frozen (immutable) to ensure thread-safety and
105
+ enable use as dictionary keys. All list-type fields use None as the
106
+ default value rather than mutable defaults like [] to avoid issues
107
+ with shared state across instances.
108
+
109
+ Parameters
110
+ ----------
111
+ name : str
112
+ Unique identifier for the agent. Must be a non-empty string.
113
+ instructions : str or Path
114
+ Plain text instructions or a path to a Jinja template file whose
115
+ contents are loaded at runtime. Required field.
116
+ description : str, optional
117
+ Short description of the agent's purpose. Default is None.
118
+ model : str, optional
119
+ Model identifier to use (e.g., "gpt-4o-mini"). Default is None.
120
+ template_path : str or Path, optional
121
+ Path to the Jinja template (absolute or relative to prompt_dir).
122
+ This takes precedence over instructions if both are provided.
123
+ Default is None.
124
+ input_structure : type[StructureBase], optional
125
+ Structure class describing the agent input. Default is None.
126
+ output_structure : type[StructureBase], optional
127
+ Structure class describing the agent output. Default is None.
128
+ tools : list, optional
129
+ Tool definitions available to the agent. Default is None.
130
+ model_settings : ModelSettings, optional
131
+ Additional model configuration settings. Default is None.
132
+ handoffs : list[Agent or Handoff], optional
133
+ List of agents or handoff configurations that this agent can
134
+ delegate to for specific tasks. Default is None.
135
+ input_guardrails : list[InputGuardrail], optional
136
+ List of guardrails to validate agent inputs before processing.
137
+ Default is None.
138
+ output_guardrails : list[OutputGuardrail], optional
139
+ List of guardrails to validate agent outputs before returning.
140
+ Default is None.
141
+ session : Session, optional
142
+ Session configuration for automatically maintaining conversation
143
+ history across agent runs. Default is None.
144
+
145
+ Methods
146
+ -------
147
+ __post_init__()
148
+ Validate configuration invariants after initialization.
149
+ instructions_text
150
+ Return the resolved instruction content as a string.
151
+ resolve_prompt_path(prompt_dir)
152
+ Resolve the prompt template path for this configuration.
153
+ gen_agent(run_context_wrapper, prompt_dir, default_model)
154
+ Create a AgentBase instance from this configuration.
155
+ replace(**changes)
156
+ Create a new AgentConfiguration with specified fields replaced.
157
+ to_json()
158
+ Return a JSON-compatible dict (inherited from JSONSerializable).
159
+ to_json_file(filepath)
160
+ Write serialized JSON data to a file (inherited from JSONSerializable).
161
+ from_json(data)
162
+ Create an instance from a JSON-compatible dict (inherited from JSONSerializable).
163
+ from_json_file(filepath)
164
+ Load an instance from a JSON file (inherited from JSONSerializable).
165
+
166
+ Examples
167
+ --------
168
+ >>> config = AgentConfiguration(
169
+ ... name="summarizer",
170
+ ... description="Summarizes text",
171
+ ... model="gpt-4o-mini"
172
+ ... )
173
+ >>> config.name
174
+ 'summarizer'
175
+ """
176
+
177
+ name: str
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
190
+ _instructions_cache: Optional[str] = field(
191
+ default=None, init=False, repr=False, compare=False
53
192
  )
54
193
 
55
- def print(self) -> str:
56
- """Return a human-readable representation.
194
+ def __post_init__(self) -> None:
195
+ """Validate configuration invariants after initialization.
196
+
197
+ Ensures that the name is a non-empty string and that instructions
198
+ are properly formatted.
199
+
200
+ Raises
201
+ ------
202
+ TypeError
203
+ If name is not a non-empty string.
204
+ If instructions is not a string or Path.
205
+ If input_structure or output_structure is not a class.
206
+ If input_structure or output_structure does not subclass StructureBase.
207
+ ValueError
208
+ If instructions is an empty string.
209
+ FileNotFoundError
210
+ If instructions is a Path that doesn't point to a readable file.
211
+ """
212
+ if not self.name or not isinstance(self.name, str):
213
+ raise TypeError("AgentConfiguration.name must be a non-empty str")
214
+
215
+ # Validate instructions (required field, like in Response module)
216
+ instructions_value = self.instructions
217
+ if isinstance(instructions_value, str):
218
+ if not instructions_value.strip():
219
+ raise ValueError(
220
+ "AgentConfiguration.instructions must be a non-empty str"
221
+ )
222
+ elif isinstance(instructions_value, Path):
223
+ instruction_path = instructions_value.expanduser()
224
+ if not instruction_path.is_file():
225
+ raise FileNotFoundError(
226
+ f"Instruction template not found: {instruction_path}"
227
+ )
228
+ else:
229
+ raise TypeError("AgentConfiguration.instructions must be a str or Path")
230
+
231
+ for attr in ("input_structure", "output_structure"):
232
+ cls = getattr(self, attr)
233
+ if cls is None:
234
+ continue
235
+ if not isinstance(cls, type):
236
+ raise TypeError(
237
+ f"AgentConfiguration.{attr} must be a class (Type[StructureBase]) or None"
238
+ )
239
+ if not issubclass(cls, StructureBase):
240
+ raise TypeError(
241
+ f"AgentConfiguration.{attr} must subclass StructureBase"
242
+ )
243
+
244
+ if self.template_path is not None and isinstance(self.template_path, Path):
245
+ # Validate template_path if it's a Path object
246
+ template = self.template_path.expanduser()
247
+ if not template.exists():
248
+ # We don't raise here because template_path might be relative
249
+ # and resolved later with prompt_dir
250
+ pass
251
+
252
+ @property
253
+ def instructions_text(self) -> str:
254
+ """Return the resolved instruction text.
57
255
 
58
256
  Returns
59
257
  -------
60
258
  str
61
- The agent's name.
259
+ Plain-text instructions, loading template files when necessary.
62
260
  """
63
- return self.name
261
+ cached = self._instructions_cache
262
+ if cached is None:
263
+ cached = self._resolve_instructions()
264
+ object.__setattr__(self, "_instructions_cache", cached)
265
+ return cached
266
+
267
+ def _resolve_instructions(self) -> str:
268
+ """Resolve instructions from string or file path."""
269
+ return resolve_instructions_from_path(self.instructions)
270
+
271
+ def resolve_prompt_path(self, prompt_dir: Path | None = None) -> Path | None:
272
+ """Resolve the prompt template path for this configuration.
273
+
274
+ Parameters
275
+ ----------
276
+ prompt_dir : Path or None, default=None
277
+ Directory holding prompt templates when a relative path is needed.
278
+
279
+ Returns
280
+ -------
281
+ Path or None
282
+ Resolved prompt path if a template is configured or discovered.
283
+ """
284
+ if self.template_path:
285
+ return Path(self.template_path)
286
+ if prompt_dir is not None:
287
+ return prompt_dir / f"{self.name}.jinja"
288
+ return None
289
+
290
+ def gen_agent(
291
+ self,
292
+ run_context_wrapper: Any = None,
293
+ prompt_dir: Path | None = None,
294
+ default_model: str | None = None,
295
+ ) -> Any:
296
+ """Create a AgentBase instance from this configuration.
297
+
298
+ This is a convenience method that instantiates ``AgentBase`` directly.
299
+
300
+ Parameters
301
+ ----------
302
+ run_context_wrapper : RunContextWrapper or None, default=None
303
+ Optional wrapper providing runtime context for prompt rendering.
304
+ prompt_dir : Path or None, default=None
305
+ Optional directory holding prompt templates.
306
+ default_model : str or None, default=None
307
+ Optional fallback model identifier if config doesn't specify one.
308
+
309
+ Returns
310
+ -------
311
+ AgentBase
312
+ Configured agent instance ready for execution.
313
+
314
+ Examples
315
+ --------
316
+ >>> config = AgentConfiguration(
317
+ ... name="helper", model="gpt-4o-mini", instructions="Help the user"
318
+ ... )
319
+ >>> agent = config.gen_agent()
320
+ >>> result = agent.run_sync("Hello!")
321
+ """
322
+ # Import here to avoid circular dependency
323
+ from .base import AgentBase
324
+
325
+ return AgentBase(
326
+ config=self,
327
+ run_context_wrapper=run_context_wrapper,
328
+ prompt_dir=prompt_dir,
329
+ default_model=default_model,
330
+ )
331
+
332
+ def replace(self, **changes: Any) -> AgentConfiguration:
333
+ """Create a new AgentConfiguration with specified fields replaced.
334
+
335
+ Since AgentConfiguration is frozen (immutable), this method creates a new
336
+ instance with the specified changes applied. This is useful for
337
+ creating variations of a configuration.
338
+
339
+ Parameters
340
+ ----------
341
+ **changes : Any
342
+ Keyword arguments specifying fields to change and their new values.
343
+
344
+ Returns
345
+ -------
346
+ AgentConfiguration
347
+ New configuration instance with changes applied.
348
+
349
+ Examples
350
+ --------
351
+ >>> config = AgentConfiguration(
352
+ ... name="agent1", model="gpt-4o-mini", instructions="Agent instructions"
353
+ ... )
354
+ >>> config2 = config.replace(name="agent2", description="Modified")
355
+ >>> config2.name
356
+ 'agent2'
357
+ >>> config2.model
358
+ 'gpt-4o-mini'
359
+ """
360
+ from dataclasses import replace
361
+
362
+ return replace(self, **changes)
363
+
364
+ def to_json(self) -> dict[str, Any]:
365
+ """Return a JSON-compatible dict representation.
366
+
367
+ Returns
368
+ -------
369
+ dict[str, Any]
370
+ Serialized configuration data without cached fields.
371
+ """
372
+ data = DataclassJSONSerializable.to_json(self)
373
+ data.pop("_instructions_cache", None)
374
+ return data
375
+
376
+ @classmethod
377
+ def from_json(cls, data: dict[str, Any]) -> AgentConfiguration:
378
+ """Create an AgentConfiguration from JSON data.
379
+
380
+ Overrides the default JSONSerializable.from_json to properly handle
381
+ the instructions field, converting string paths that look like file
382
+ paths back to Path objects for proper file loading.
383
+
384
+ Parameters
385
+ ----------
386
+ data : dict[str, Any]
387
+ Dictionary containing the configuration data.
388
+
389
+ Returns
390
+ -------
391
+ AgentConfiguration
392
+ New configuration instance.
393
+
394
+ Notes
395
+ -----
396
+ This method attempts to preserve the original type of the instructions
397
+ field. If instructions is a string that represents an existing file path,
398
+ it will be converted to a Path object to ensure proper file loading
399
+ behavior is maintained across JSON round-trips.
400
+ """
401
+ # Make a copy to avoid modifying the input
402
+ data = data.copy()
403
+ data.pop("_instructions_cache", None)
404
+
405
+ # Handle instructions field: if it's a string path to an existing file,
406
+ # convert it back to Path for proper file loading
407
+ if "instructions" in data and data["instructions"] is not None:
408
+ instructions_value = data["instructions"]
409
+ if isinstance(instructions_value, str):
410
+ # Check if it looks like a file path and the file exists
411
+ # This preserves the intended behavior for file-based instructions
412
+ try:
413
+ potential_path = Path(instructions_value)
414
+ # Only convert to Path if it's an existing file
415
+ # This way, plain text instructions stay as strings
416
+ if potential_path.exists() and potential_path.is_file():
417
+ data["instructions"] = potential_path
418
+ except (OSError, ValueError):
419
+ # If path parsing fails, keep it as a string (likely plain text)
420
+ pass
421
+
422
+ # Use the parent class method for the rest
423
+ return super(AgentConfiguration, cls).from_json(data)
64
424
 
65
425
 
66
- __all__ = ["AgentConfig"]
426
+ # Global default registry instance
427
+ _default_registry = AgentConfigurationRegistry()
67
428
 
68
- AgentConfig.model_rebuild()
429
+ __all__ = ["AgentConfiguration", "AgentConfigurationRegistry", "get_default_registry"]
@@ -12,9 +12,9 @@ from typing import Any, Callable, Dict, List, Optional
12
12
 
13
13
 
14
14
  from ..structure import TaskStructure, PlanStructure, PromptStructure
15
- from ..utils import JSONSerializable, ensure_directory, log
15
+ from ..utils import DataclassJSONSerializable, ensure_directory, log
16
16
  from .base import AgentBase
17
- from .config import AgentConfig
17
+ from .config import AgentConfiguration
18
18
  from ..structure.plan.enum import AgentEnum
19
19
 
20
20
  PromptFn = Callable[[str], PromptStructure]
@@ -23,9 +23,30 @@ ExecutePlanFn = Callable[[PlanStructure], List[str]]
23
23
  SummarizeFn = Callable[[List[str]], str]
24
24
 
25
25
 
26
- class CoordinatorAgent(AgentBase, JSONSerializable):
26
+ class CoordinatorAgent(AgentBase):
27
27
  """Coordinate agent plans while persisting project state and outputs.
28
28
 
29
+ Parameters
30
+ ----------
31
+ prompt_fn : PromptFn
32
+ Callable that generates a prompt brief from the input string.
33
+ build_plan_fn : BuildPlanFn
34
+ Callable that generates a plan from the prompt brief.
35
+ execute_plan_fn : ExecutePlanFn
36
+ Callable that executes a plan and returns results.
37
+ summarize_fn : SummarizeFn
38
+ Callable that summarizes a list of result strings.
39
+ module_data_path : Path
40
+ Base path for persisting project artifacts.
41
+ name : str
42
+ Name of the parent module for data organization.
43
+ config : AgentConfiguration or None, default=None
44
+ Optional agent configuration describing prompts and metadata.
45
+ prompt_dir : Path or None, default=None
46
+ Optional directory holding prompt templates.
47
+ default_model : str or None, default=None
48
+ Optional fallback model identifier.
49
+
29
50
  Methods
30
51
  -------
31
52
  build_prompt(prompt)
@@ -63,7 +84,7 @@ class CoordinatorAgent(AgentBase, JSONSerializable):
63
84
  summarize_fn: SummarizeFn,
64
85
  module_data_path: Path,
65
86
  name: str,
66
- config: Optional[AgentConfig] = None,
87
+ config: Optional[AgentConfiguration] = None,
67
88
  prompt_dir: Optional[Path] = None,
68
89
  default_model: Optional[str] = None,
69
90
  ) -> None:
@@ -83,16 +104,33 @@ class CoordinatorAgent(AgentBase, JSONSerializable):
83
104
  Base path for persisting project artifacts.
84
105
  name : str
85
106
  Name of the parent module for data organization.
86
- config : AgentConfig or None, default=None
107
+ config : AgentConfiguration or None, default=None
87
108
  Optional agent configuration describing prompts and metadata.
88
109
  prompt_dir : Path or None, default=None
89
110
  Optional directory holding prompt templates.
90
111
  default_model : str or None, default=None
91
112
  Optional fallback model identifier.
113
+
114
+ Raises
115
+ ------
116
+ ValueError
117
+ If the provided configuration is invalid.
118
+
119
+ Examples
120
+ --------
121
+ >>> coordinator = CoordinatorAgent(
122
+ ... prompt_fn=lambda p: PromptStructure(prompt=p),
123
+ ... build_plan_fn=lambda p: PlanStructure(),
124
+ ... execute_plan_fn=lambda p: [],
125
+ ... summarize_fn=lambda r: "summary",
126
+ ... module_data_path=Path("."),
127
+ ... name="test",
128
+ ... )
92
129
  """
93
130
  if config is None:
94
- config = AgentConfig(
131
+ config = AgentConfiguration(
95
132
  name="coordinator_agent",
133
+ instructions="Coordinate agents for planning and summarization.",
96
134
  description="Coordinates agents for planning and summarization.",
97
135
  )
98
136
  super().__init__(
@@ -119,6 +157,10 @@ class CoordinatorAgent(AgentBase, JSONSerializable):
119
157
  ----------
120
158
  prompt : str
121
159
  The core request or goal for the project.
160
+
161
+ Examples
162
+ --------
163
+ >>> coordinator.build_prompt("Analyze the impact of AI on healthcare.")
122
164
  """
123
165
  log("build_prompt", level=logging.INFO)
124
166
  self.start_date = datetime.now(timezone.utc)
@@ -133,6 +175,11 @@ class CoordinatorAgent(AgentBase, JSONSerializable):
133
175
  ------
134
176
  ValueError
135
177
  If called before :meth:`build_prompt`.
178
+
179
+ Examples
180
+ --------
181
+ >>> coordinator.build_prompt("Analyze AI in healthcare.")
182
+ >>> coordinator.build_plan()
136
183
  """
137
184
  log("build_plan", level=logging.INFO)
138
185
  if not self.brief:
@@ -150,6 +197,12 @@ class CoordinatorAgent(AgentBase, JSONSerializable):
150
197
  -------
151
198
  list[str]
152
199
  Flattened list of results from all executed tasks.
200
+
201
+ Examples
202
+ --------
203
+ >>> coordinator.build_prompt("Analyze AI.")
204
+ >>> coordinator.build_plan()
205
+ >>> results = coordinator.execute_plan()
153
206
  """
154
207
  log("execute_plan", level=logging.INFO)
155
208
  if not self.plan:
@@ -173,6 +226,11 @@ class CoordinatorAgent(AgentBase, JSONSerializable):
173
226
  -------
174
227
  str
175
228
  Concise summary derived from the provided results.
229
+
230
+ Examples
231
+ --------
232
+ >>> results = ["AI is impacting healthcare.", "New models are faster."]
233
+ >>> summary = coordinator.summarize_plan(results)
176
234
  """
177
235
  log("summarize_plan", level=logging.INFO)
178
236
 
@@ -198,37 +256,16 @@ class CoordinatorAgent(AgentBase, JSONSerializable):
198
256
  ----------
199
257
  prompt : str
200
258
  The request or question to analyze and summarize.
259
+
260
+ Examples
261
+ --------
262
+ >>> coordinator.run_plan("Analyze the future of AI.")
201
263
  """
202
264
  self.build_prompt(prompt)
203
265
  self.build_plan()
204
266
  results = self.execute_plan()
205
267
  self.summarize_plan(results)
206
268
 
207
- @property
208
- def file_path(self) -> Path:
209
- """Return the path where the project snapshot will be stored.
210
-
211
- Returns
212
- -------
213
- Path
214
- Location of the JSON artifact for the current run.
215
- """
216
- if not self.start_date:
217
- self.start_date = datetime.now(timezone.utc)
218
- start_date_str = self.start_date.strftime("%Y%m%d_%H%M%S")
219
- return self._module_data_path / self._name / f"{start_date_str}.json"
220
-
221
- def save(self) -> Path:
222
- """Persist the current project state to disk.
223
-
224
- Returns
225
- -------
226
- Path
227
- Path to the saved JSON artifact.
228
- """
229
- self.to_json_file(self.file_path)
230
- return self.file_path
231
-
232
269
  @staticmethod
233
270
  def _run_task(
234
271
  task: TaskStructure,