openai-sdk-helpers 0.0.2__py3-none-any.whl → 0.0.3__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 (45) hide show
  1. openai_sdk_helpers/prompt/summarizer.jinja +7 -0
  2. openai_sdk_helpers/prompt/translator.jinja +7 -0
  3. openai_sdk_helpers/prompt/validator.jinja +7 -0
  4. openai_sdk_helpers/py.typed +0 -0
  5. {openai_sdk_helpers-0.0.2.dist-info → openai_sdk_helpers-0.0.3.dist-info}/METADATA +57 -4
  6. openai_sdk_helpers-0.0.3.dist-info/RECORD +8 -0
  7. openai_sdk_helpers/__init__.py +0 -34
  8. openai_sdk_helpers/agent/__init__.py +0 -23
  9. openai_sdk_helpers/agent/base.py +0 -432
  10. openai_sdk_helpers/agent/config.py +0 -66
  11. openai_sdk_helpers/agent/project_manager.py +0 -416
  12. openai_sdk_helpers/agent/runner.py +0 -117
  13. openai_sdk_helpers/agent/utils.py +0 -47
  14. openai_sdk_helpers/agent/vector_search.py +0 -418
  15. openai_sdk_helpers/agent/web_search.py +0 -404
  16. openai_sdk_helpers/config.py +0 -141
  17. openai_sdk_helpers/enums/__init__.py +0 -7
  18. openai_sdk_helpers/enums/base.py +0 -17
  19. openai_sdk_helpers/environment.py +0 -27
  20. openai_sdk_helpers/prompt/__init__.py +0 -77
  21. openai_sdk_helpers/response/__init__.py +0 -16
  22. openai_sdk_helpers/response/base.py +0 -477
  23. openai_sdk_helpers/response/messages.py +0 -211
  24. openai_sdk_helpers/response/runner.py +0 -42
  25. openai_sdk_helpers/response/tool_call.py +0 -70
  26. openai_sdk_helpers/structure/__init__.py +0 -57
  27. openai_sdk_helpers/structure/base.py +0 -591
  28. openai_sdk_helpers/structure/plan/__init__.py +0 -13
  29. openai_sdk_helpers/structure/plan/enum.py +0 -48
  30. openai_sdk_helpers/structure/plan/plan.py +0 -104
  31. openai_sdk_helpers/structure/plan/task.py +0 -122
  32. openai_sdk_helpers/structure/prompt.py +0 -24
  33. openai_sdk_helpers/structure/responses.py +0 -148
  34. openai_sdk_helpers/structure/summary.py +0 -65
  35. openai_sdk_helpers/structure/vector_search.py +0 -82
  36. openai_sdk_helpers/structure/web_search.py +0 -46
  37. openai_sdk_helpers/utils/__init__.py +0 -13
  38. openai_sdk_helpers/utils/core.py +0 -208
  39. openai_sdk_helpers/vector_storage/__init__.py +0 -15
  40. openai_sdk_helpers/vector_storage/cleanup.py +0 -91
  41. openai_sdk_helpers/vector_storage/storage.py +0 -501
  42. openai_sdk_helpers/vector_storage/types.py +0 -58
  43. openai_sdk_helpers-0.0.2.dist-info/RECORD +0 -40
  44. {openai_sdk_helpers-0.0.2.dist-info → openai_sdk_helpers-0.0.3.dist-info}/WHEEL +0 -0
  45. {openai_sdk_helpers-0.0.2.dist-info → openai_sdk_helpers-0.0.3.dist-info}/licenses/LICENSE +0 -0
@@ -1,416 +0,0 @@
1
- """Generic project manager for coordinating agent plans."""
2
-
3
- from __future__ import annotations
4
-
5
- import asyncio
6
- import inspect
7
- import logging
8
- import threading
9
- from datetime import datetime
10
- from pathlib import Path
11
- from typing import Any, Callable, Dict, List, Optional
12
-
13
- from ..structure import AgentEnum, AgentTaskStructure, PlanStructure, PromptStructure
14
- from ..environment import DATETIME_FMT
15
- from ..utils import JSONSerializable, log
16
- from .base import BaseAgent
17
- from .config import AgentConfig
18
-
19
- BuildBriefFn = Callable[[str], PromptStructure]
20
- BuildPlanFn = Callable[[str], PlanStructure]
21
- ExecutePlanFn = Callable[[PlanStructure], List[str]]
22
- SummarizeFn = Callable[[List[str]], str]
23
-
24
-
25
- class ProjectManager(BaseAgent, JSONSerializable):
26
- """Coordinate agent plans while persisting project state and outputs.
27
-
28
- Methods
29
- -------
30
- build_instructions(prompt)
31
- Summarize the prompt into a concise brief.
32
- build_plan()
33
- Create a list of ``AgentTaskStructure`` entries for the project.
34
- execute_plan()
35
- Run each task sequentially while tracking status and timing.
36
- summarize_plan(results)
37
- Summarize a collection of result strings.
38
- to_dict()
39
- Return a JSON-serializable snapshot of stored project data.
40
- save()
41
- Persist the stored project data to a JSON file.
42
- """
43
-
44
- def __init__(
45
- self,
46
- build_brief_fn: BuildBriefFn,
47
- build_plan_fn: BuildPlanFn,
48
- execute_plan_fn: ExecutePlanFn,
49
- summarize_fn: SummarizeFn,
50
- module_data_path: Path,
51
- module_name: str,
52
- config: Optional[AgentConfig] = None,
53
- prompt_dir: Optional[Path] = None,
54
- default_model: Optional[str] = None,
55
- ) -> None:
56
- """Initialize the project manager with injected workflow helpers.
57
-
58
- Parameters
59
- ----------
60
- build_brief_fn
61
- Callable that generates a prompt brief from the input string.
62
- build_plan_fn
63
- Callable that generates a plan from the prompt brief.
64
- execute_plan_fn
65
- Callable that executes a plan and returns results.
66
- summarize_fn
67
- Callable that summarizes a list of result strings.
68
- module_data_path
69
- Base path for persisting project artifacts.
70
- module_name
71
- Name of the parent module for data organization.
72
- config
73
- Optional agent configuration describing prompts and metadata.
74
- prompt_dir
75
- Optional directory holding prompt templates.
76
- default_model
77
- Optional fallback model identifier.
78
-
79
- Returns
80
- -------
81
- None
82
- """
83
- if config is None:
84
- config = AgentConfig(
85
- name="project_manager",
86
- description="Coordinates agents for planning and summarization.",
87
- )
88
- super().__init__(
89
- config=config, prompt_dir=prompt_dir, default_model=default_model
90
- )
91
- self._build_brief_fn = build_brief_fn
92
- self._build_plan_fn = build_plan_fn
93
- self._execute_plan_fn = execute_plan_fn
94
- self._summarize_fn = summarize_fn
95
- self._module_data_path = Path(module_data_path)
96
- self._module_name = module_name
97
-
98
- self.prompt: Optional[str] = None
99
- self.brief: Optional[PromptStructure] = None
100
- self.plan: PlanStructure = PlanStructure()
101
- self.summary: Optional[str] = None
102
- self.start_date: Optional[datetime] = None
103
- self.end_date: Optional[datetime] = None
104
-
105
- def build_instructions(self, prompt: str) -> None:
106
- """Return a concise brief for the project.
107
-
108
- Parameters
109
- ----------
110
- prompt : str
111
- The core request or goal for the project.
112
-
113
- Returns
114
- -------
115
- None
116
- """
117
- log("build_instructions", level=logging.INFO)
118
- self.start_date = datetime.utcnow()
119
- self.prompt = prompt
120
- self.brief = self._build_brief_fn(prompt)
121
- self.save()
122
-
123
- def build_plan(self) -> None:
124
- """Generate and store a structured plan based on the current brief.
125
-
126
- Raises
127
- ------
128
- ValueError
129
- If called before :meth:`build_instructions`.
130
-
131
- Returns
132
- -------
133
- None
134
- """
135
- log("build_plan", level=logging.INFO)
136
- if not self.brief:
137
- raise ValueError("Brief is required before building a plan.")
138
-
139
- plan = self._build_plan_fn(self.brief.prompt)
140
- if isinstance(plan, PlanStructure):
141
- self.plan = plan
142
- self.save()
143
-
144
- def execute_plan(self) -> List[str]:
145
- """Run each task, updating status, timestamps, and recorded results.
146
-
147
- Returns
148
- -------
149
- list[str]
150
- Flattened list of results from all executed tasks.
151
- """
152
- log("execute_plan", level=logging.INFO)
153
- if not self.plan:
154
- log("No tasks to execute.", level=logging.WARNING)
155
- return []
156
-
157
- compiled_results = self._execute_plan_fn(self.plan)
158
- self.save()
159
- return compiled_results
160
-
161
- def summarize_plan(self, results: Optional[List[str]] = None) -> str:
162
- """Summarize a collection of task outputs.
163
-
164
- Parameters
165
- ----------
166
- results : list[str], optional
167
- List of string outputs gathered from task execution. Defaults to
168
- ``None``, which uses the stored plan task results if available.
169
-
170
- Returns
171
- -------
172
- str
173
- Concise summary derived from the provided results.
174
- """
175
- log("summarize_plan", level=logging.INFO)
176
-
177
- if results is None:
178
- results = []
179
- if self.plan and self.plan.tasks:
180
- for task in self.plan.tasks:
181
- results.extend(task.results or [])
182
-
183
- if not results:
184
- self.summary = ""
185
- return self.summary
186
-
187
- self.summary = self._summarize_fn(results)
188
- self.end_date = datetime.utcnow()
189
- self.save()
190
- return self.summary
191
-
192
- def run_plan(self, prompt: str) -> None:
193
- """Execute the full workflow for the provided prompt.
194
-
195
- Parameters
196
- ----------
197
- prompt : str
198
- The request or question to analyze and summarize.
199
-
200
- Returns
201
- -------
202
- None
203
- """
204
- self.build_instructions(prompt)
205
- self.build_plan()
206
- results = self.execute_plan()
207
- self.summarize_plan(results)
208
-
209
- @property
210
- def file_path(self) -> Path:
211
- """Return the path where the project snapshot will be stored.
212
-
213
- Returns
214
- -------
215
- Path
216
- Location of the JSON artifact for the current run.
217
- """
218
- if not self.start_date:
219
- self.start_date = datetime.utcnow()
220
- start_date_str = self.start_date.strftime(DATETIME_FMT)
221
- return self._module_data_path / self._module_name / f"{start_date_str}.json"
222
-
223
- def save(self) -> Path:
224
- """Persist the current project state to disk.
225
-
226
- Returns
227
- -------
228
- Path
229
- Path to the saved JSON artifact.
230
- """
231
- self.to_json_file(self.file_path)
232
- return self.file_path
233
-
234
- @staticmethod
235
- def _run_task(
236
- task: AgentTaskStructure,
237
- agent_callable: Callable[..., Any],
238
- aggregated_context: List[str],
239
- ) -> Any:
240
- """Execute a single task and return the raw result.
241
-
242
- Parameters
243
- ----------
244
- task : AgentTaskStructure
245
- Task definition containing the callable and inputs.
246
- aggregated_context : list[str]
247
- Context combined from the task and prior task outputs.
248
-
249
- Returns
250
- -------
251
- Any
252
- Raw output from the underlying callable.
253
- """
254
- task_type = ProjectManager._normalize_task_type(task.task_type)
255
- prompt_with_context = task.prompt
256
- if aggregated_context and task_type not in {"WebAgentSearch", "VectorSearch"}:
257
- context_block = "\n".join(aggregated_context)
258
- prompt_with_context = f"{task.prompt}\n\nContext:\n{context_block}"
259
-
260
- try:
261
- if task_type == "summarizer":
262
- summary_chunks: List[str] = [task.prompt] + aggregated_context
263
- output = agent_callable(summary_chunks)
264
- elif task_type in {"WebAgentSearch", "VectorSearch"}:
265
- output = agent_callable(task.prompt)
266
- else:
267
- output = agent_callable(
268
- prompt_with_context,
269
- context=aggregated_context,
270
- )
271
- except TypeError:
272
- output = agent_callable(prompt_with_context)
273
- except Exception as exc: # pragma: no cover - defensive guard
274
- log(
275
- f"Task '{task.task_type}' encountered an error: {exc}",
276
- level=logging.ERROR,
277
- )
278
- return f"Task error: {exc}"
279
- return ProjectManager._resolve_result(output)
280
-
281
- @staticmethod
282
- def _run_task_in_thread(
283
- task: AgentTaskStructure,
284
- agent_callable: Callable[..., Any],
285
- aggregated_context: List[str],
286
- ) -> Any:
287
- """Execute a task in a background thread to avoid event-loop conflicts."""
288
- result_container: Dict[str, Any] = {"result": None, "error": None}
289
-
290
- def _runner() -> None:
291
- try:
292
- result_container["result"] = ProjectManager._run_task(
293
- task,
294
- agent_callable=agent_callable,
295
- aggregated_context=aggregated_context,
296
- )
297
- except Exception as exc: # pragma: no cover - defensive guard
298
- result_container["error"] = exc
299
-
300
- thread = threading.Thread(target=_runner)
301
- thread.start()
302
- thread.join()
303
- if result_container["error"] is not None:
304
- raise result_container["error"]
305
- return result_container["result"]
306
-
307
- @staticmethod
308
- def _resolve_result(result: Any) -> Any:
309
- """Return awaited results when the callable is asynchronous.
310
-
311
- Parameters
312
- ----------
313
- result : Any
314
- Potentially awaitable output from a task callable.
315
-
316
- Returns
317
- -------
318
- Any
319
- Resolved output, awaited when necessary.
320
- """
321
- if not inspect.isawaitable(result):
322
- return result
323
-
324
- awaitable: asyncio.Future[Any] | asyncio.Task[Any] | Any = result
325
- coroutine = (
326
- awaitable
327
- if inspect.iscoroutine(awaitable)
328
- else ProjectManager._await_wrapper(awaitable)
329
- )
330
-
331
- try:
332
- loop = asyncio.get_running_loop()
333
- except RuntimeError:
334
- return asyncio.run(coroutine)
335
-
336
- if loop.is_running():
337
- resolved_result: Any = None
338
-
339
- def _run_in_thread() -> None:
340
- nonlocal resolved_result
341
- resolved_result = asyncio.run(coroutine)
342
-
343
- thread = threading.Thread(target=_run_in_thread, daemon=True)
344
- thread.start()
345
- thread.join()
346
- return resolved_result
347
-
348
- return loop.run_until_complete(coroutine)
349
-
350
- @staticmethod
351
- async def _await_wrapper(awaitable: Any) -> Any:
352
- """Await a generic awaitable and return its result."""
353
- return await awaitable
354
-
355
- @staticmethod
356
- def _normalize_results(result: Any) -> List[str]:
357
- """Convert agent outputs into a list of strings.
358
-
359
- Parameters
360
- ----------
361
- result : Any
362
- Raw output from a task execution.
363
-
364
- Returns
365
- -------
366
- list[str]
367
- Normalized string values representing the output.
368
- """
369
- if result is None:
370
- return []
371
- if isinstance(result, list):
372
- return [str(item) for item in result]
373
- return [str(result)]
374
-
375
- def _persist_task_results(self, task: AgentTaskStructure) -> Path:
376
- """Write task context and results to disk for future analysis."""
377
- run_dir = self._get_run_directory()
378
- task_label = self._task_label(task)
379
- file_path = run_dir / f"{task_label}.json"
380
- task.to_json_file(str(file_path))
381
- return file_path
382
-
383
- def _get_run_directory(self) -> Path:
384
- """Return (and create) the directory used to persist task artifacts."""
385
- if not hasattr(self, "_run_directory"):
386
- timestamp = datetime.utcnow().strftime("%Y%m%d_%H%M%S")
387
- self._run_directory = (
388
- self._module_data_path
389
- / Path(self._module_name)
390
- / "project_manager"
391
- / timestamp
392
- )
393
- self._run_directory.mkdir(parents=True, exist_ok=True)
394
- return self._run_directory
395
-
396
- @staticmethod
397
- def _task_label(task: AgentTaskStructure) -> str:
398
- """Generate a filesystem-safe label for the task."""
399
- task_type = ProjectManager._normalize_task_type(task.task_type)
400
- base = (task_type or "task").replace(" ", "_").lower()
401
- return f"{base}_{task_type}"
402
-
403
- @staticmethod
404
- def _normalize_task_type(task_type: AgentEnum | str) -> str:
405
- """Return the normalized task type string."""
406
- if isinstance(task_type, AgentEnum):
407
- return task_type.value
408
- if task_type in AgentEnum.__members__:
409
- return AgentEnum.__members__[task_type].value
410
- try:
411
- return AgentEnum(task_type).value
412
- except ValueError:
413
- return str(task_type)
414
-
415
-
416
- __all__ = ["ProjectManager"]
@@ -1,117 +0,0 @@
1
- """Convenience wrappers for running OpenAI agents.
2
-
3
- These helpers provide a narrow surface around the lower-level functions in
4
- ``openai-sdk-helpers.agent.base`` so that callers can execute agents with consistent
5
- signatures whether they need asynchronous, synchronous, or streamed results.
6
- """
7
-
8
- from __future__ import annotations
9
-
10
- from typing import Any, Dict, Optional
11
-
12
- from agents import Agent, RunResult, RunResultStreaming
13
-
14
- from .base import _run_agent, _run_agent_streamed, _run_agent_sync
15
-
16
-
17
- async def run(
18
- agent: Agent,
19
- agent_input: str,
20
- agent_context: Optional[Dict[str, Any]] = None,
21
- output_type: Optional[Any] = None,
22
- ) -> Any:
23
- """Run an ``Agent`` asynchronously.
24
-
25
- Parameters
26
- ----------
27
- agent
28
- Configured agent instance to execute.
29
- agent_input
30
- Prompt or query string for the agent.
31
- agent_context
32
- Optional context dictionary passed to the agent. Default ``None``.
33
- output_type
34
- Optional type used to cast the final output. Default ``None``.
35
-
36
- Returns
37
- -------
38
- Any
39
- Agent response, optionally converted to ``output_type``.
40
- """
41
- return await _run_agent(
42
- agent=agent,
43
- agent_input=agent_input,
44
- agent_context=agent_context,
45
- output_type=output_type,
46
- )
47
-
48
-
49
- def run_sync(
50
- agent: Agent,
51
- agent_input: str,
52
- agent_context: Optional[Dict[str, Any]] = None,
53
- output_type: Optional[Any] = None,
54
- ) -> Any:
55
- """Run an ``Agent`` synchronously.
56
-
57
- Parameters
58
- ----------
59
- agent
60
- Configured agent instance to execute.
61
- agent_input
62
- Prompt or query string for the agent.
63
- agent_context
64
- Optional context dictionary passed to the agent. Default ``None``.
65
- output_type
66
- Optional type used to cast the final output. Default ``None``.
67
-
68
- Returns
69
- -------
70
- Any
71
- Agent response, optionally converted to ``output_type``.
72
- """
73
- result: RunResult = _run_agent_sync(
74
- agent=agent,
75
- agent_input=agent_input,
76
- agent_context=agent_context,
77
- )
78
- if output_type:
79
- return result.final_output_as(output_type)
80
- return result
81
-
82
-
83
- def run_streamed(
84
- agent: Agent,
85
- agent_input: str,
86
- agent_context: Optional[Dict[str, Any]] = None,
87
- output_type: Optional[Any] = None,
88
- ) -> RunResultStreaming:
89
- """Run an ``Agent`` and return a streaming result.
90
-
91
- Parameters
92
- ----------
93
- agent
94
- Configured agent instance to execute.
95
- agent_input
96
- Prompt or query string for the agent.
97
- agent_context
98
- Optional context dictionary passed to the agent. Default ``None``.
99
- output_type
100
- Optional type used to cast the final output. Default ``None``.
101
-
102
- Returns
103
- -------
104
- RunResultStreaming
105
- Streaming output wrapper from the agent execution.
106
- """
107
- result = _run_agent_streamed(
108
- agent=agent,
109
- agent_input=agent_input,
110
- context=agent_context,
111
- )
112
- if output_type:
113
- return result.final_output_as(output_type)
114
- return result
115
-
116
-
117
- __all__ = ["run", "run_sync", "run_streamed"]
@@ -1,47 +0,0 @@
1
- """Utility helpers for synchronous interaction with async agents."""
2
-
3
- from __future__ import annotations
4
-
5
- import asyncio
6
- import threading
7
- from typing import Any, Coroutine, TypeVar
8
-
9
- T = TypeVar("T")
10
-
11
-
12
- def run_coro_sync(coro: Coroutine[Any, Any, T]) -> T:
13
- """Run a coroutine from synchronous code.
14
-
15
- Parameters
16
- ----------
17
- coro : Coroutine[Any, Any, T]
18
- Coroutine to execute.
19
-
20
- Returns
21
- -------
22
- T
23
- Result returned by the coroutine.
24
- """
25
- try:
26
- loop = asyncio.get_running_loop()
27
- except RuntimeError:
28
- return asyncio.run(coro)
29
-
30
- if loop.is_running():
31
- result: T | None = None
32
-
33
- def _thread_runner() -> None:
34
- nonlocal result
35
- result = asyncio.run(coro)
36
-
37
- thread = threading.Thread(target=_thread_runner, daemon=True)
38
- thread.start()
39
- thread.join()
40
- if result is None:
41
- raise RuntimeError("Coroutine execution did not return a result.")
42
- return result
43
-
44
- return loop.run_until_complete(coro)
45
-
46
-
47
- __all__ = ["run_coro_sync"]