openai-sdk-helpers 0.6.5__tar.gz → 0.7.0__tar.gz

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 (99) hide show
  1. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/PKG-INFO +1 -1
  2. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/pyproject.toml +1 -1
  3. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/agent/classifier.py +48 -4
  4. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/response/base.py +13 -13
  5. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/response/configuration.py +8 -0
  6. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/structure/classification.py +49 -0
  7. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/.gitignore +0 -0
  8. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/LICENSE +0 -0
  9. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/README.md +0 -0
  10. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/__init__.py +0 -0
  11. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/agent/__init__.py +0 -0
  12. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/agent/base.py +0 -0
  13. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/agent/configuration.py +0 -0
  14. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/agent/coordinator.py +0 -0
  15. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/agent/files.py +0 -0
  16. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/agent/runner.py +0 -0
  17. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/agent/search/__init__.py +0 -0
  18. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/agent/search/base.py +0 -0
  19. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/agent/search/vector.py +0 -0
  20. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/agent/search/web.py +0 -0
  21. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/agent/summarizer.py +0 -0
  22. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/agent/translator.py +0 -0
  23. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/agent/utils.py +0 -0
  24. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/agent/validator.py +0 -0
  25. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/cli.py +0 -0
  26. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/enums/__init__.py +0 -0
  27. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/enums/base.py +0 -0
  28. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/environment.py +0 -0
  29. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/errors.py +0 -0
  30. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/extract/__init__.py +0 -0
  31. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/extract/extractor.py +0 -0
  32. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/extract/generator.py +0 -0
  33. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/files_api.py +0 -0
  34. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/logging.py +0 -0
  35. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/prompt/__init__.py +0 -0
  36. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/prompt/base.py +0 -0
  37. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/prompt/classifier.jinja +0 -0
  38. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/prompt/extractor_config_agent_instructions.jinja +0 -0
  39. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/prompt/extractor_config_generator.jinja +0 -0
  40. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/prompt/extractor_config_generator_instructions.jinja +0 -0
  41. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/prompt/extractor_prompt_optimizer_agent_instructions.jinja +0 -0
  42. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/prompt/extractor_prompt_optimizer_request.jinja +0 -0
  43. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/prompt/summarizer.jinja +0 -0
  44. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/prompt/translator.jinja +0 -0
  45. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/prompt/validator.jinja +0 -0
  46. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/prompt/vector_planner.jinja +0 -0
  47. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/prompt/vector_search.jinja +0 -0
  48. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/prompt/vector_writer.jinja +0 -0
  49. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/py.typed +0 -0
  50. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/response/__init__.py +0 -0
  51. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/response/files.py +0 -0
  52. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/response/messages.py +0 -0
  53. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/response/planner.py +0 -0
  54. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/response/prompter.py +0 -0
  55. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/response/runner.py +0 -0
  56. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/response/tool_call.py +0 -0
  57. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/response/vector_store.py +0 -0
  58. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/settings.py +0 -0
  59. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/streamlit_app/__init__.py +0 -0
  60. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/streamlit_app/app.py +0 -0
  61. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/streamlit_app/configuration.py +0 -0
  62. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/structure/__init__.py +0 -0
  63. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/structure/agent_blueprint.py +0 -0
  64. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/structure/base.py +0 -0
  65. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/structure/extraction.py +0 -0
  66. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/structure/plan/__init__.py +0 -0
  67. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/structure/plan/enum.py +0 -0
  68. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/structure/plan/helpers.py +0 -0
  69. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/structure/plan/plan.py +0 -0
  70. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/structure/plan/task.py +0 -0
  71. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/structure/plan/types.py +0 -0
  72. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/structure/prompt.py +0 -0
  73. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/structure/responses.py +0 -0
  74. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/structure/summary.py +0 -0
  75. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/structure/translation.py +0 -0
  76. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/structure/validation.py +0 -0
  77. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/structure/vector_search.py +0 -0
  78. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/structure/web_search.py +0 -0
  79. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/tools.py +0 -0
  80. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/types.py +0 -0
  81. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/utils/__init__.py +0 -0
  82. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/utils/async_utils.py +0 -0
  83. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/utils/coercion.py +0 -0
  84. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/utils/encoding.py +0 -0
  85. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/utils/instructions.py +0 -0
  86. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/utils/json/__init__.py +0 -0
  87. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/utils/json/base_model.py +0 -0
  88. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/utils/json/data_class.py +0 -0
  89. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/utils/json/ref.py +0 -0
  90. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/utils/json/utils.py +0 -0
  91. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/utils/langextract.py +0 -0
  92. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/utils/output_validation.py +0 -0
  93. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/utils/path_utils.py +0 -0
  94. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/utils/registry.py +0 -0
  95. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/utils/validation.py +0 -0
  96. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/vector_storage/__init__.py +0 -0
  97. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/vector_storage/cleanup.py +0 -0
  98. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/vector_storage/storage.py +0 -0
  99. {openai_sdk_helpers-0.6.5 → openai_sdk_helpers-0.7.0}/src/openai_sdk_helpers/vector_storage/types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openai-sdk-helpers
3
- Version: 0.6.5
3
+ Version: 0.7.0
4
4
  Summary: Composable helpers for OpenAI SDK agents, prompts, and storage
5
5
  Author: openai-sdk-helpers maintainers
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "openai-sdk-helpers"
3
- version = "0.6.5"
3
+ version = "0.7.0"
4
4
  requires-python = ">=3.10"
5
5
  readme = "README.md"
6
6
  description = "Composable helpers for OpenAI SDK agents, prompts, and storage"
@@ -26,6 +26,8 @@ from ..utils import ensure_list
26
26
  from .base import AgentBase
27
27
  from .configuration import AgentConfiguration
28
28
 
29
+ _CONTINUE_CONFIDENCE_THRESHOLD = 0.7
30
+
29
31
 
30
32
  class TaxonomyClassifierAgent(AgentBase):
31
33
  """Classify text by recursively traversing a taxonomy.
@@ -394,6 +396,13 @@ class TaxonomyClassifierAgent(AgentBase):
394
396
 
395
397
  resolved_nodes = _resolve_nodes(node_paths, step)
396
398
 
399
+ should_continue = _should_continue_from_stop(step, resolved_nodes)
400
+ if should_continue:
401
+ step = step.model_copy(
402
+ update={"stop_reason": ClassificationStopReason.CONTINUE}
403
+ )
404
+ state.steps[-1] = step
405
+
397
406
  if step.stop_reason.is_terminal:
398
407
  if resolved_nodes:
399
408
  state.final_nodes.extend(resolved_nodes)
@@ -407,7 +416,7 @@ class TaxonomyClassifierAgent(AgentBase):
407
416
  return
408
417
 
409
418
  base_steps_len = len(state.steps)
410
- child_tasks: list[tuple[Awaitable["_TraversalState"], int]] = []
419
+ child_tasks: list[tuple[Awaitable["_TraversalState"], int, TaxonomyNode]] = []
411
420
  for node in resolved_nodes:
412
421
  if node.children:
413
422
  sub_agent = self._build_sub_agent(list(node.children))
@@ -429,6 +438,7 @@ class TaxonomyClassifierAgent(AgentBase):
429
438
  state=sub_state,
430
439
  ),
431
440
  base_final_nodes_len,
441
+ node,
432
442
  )
433
443
  )
434
444
  else:
@@ -439,13 +449,19 @@ class TaxonomyClassifierAgent(AgentBase):
439
449
  )
440
450
  if child_tasks:
441
451
  child_states = await asyncio.gather(
442
- *(child_task for child_task, _ in child_tasks)
452
+ *(child_task for child_task, _, _ in child_tasks)
443
453
  )
444
- for child_state, (_, base_final_nodes_len) in zip(
454
+ for child_state, (_, base_final_nodes_len, parent_node) in zip(
445
455
  child_states, child_tasks, strict=True
446
456
  ):
447
457
  state.steps.extend(child_state.steps[base_steps_len:])
448
- state.final_nodes.extend(child_state.final_nodes[base_final_nodes_len:])
458
+ child_final_nodes = child_state.final_nodes[base_final_nodes_len:]
459
+ state.final_nodes.extend(child_final_nodes)
460
+ if should_continue and not child_final_nodes:
461
+ state.final_nodes.append(parent_node)
462
+ state.best_confidence = _max_confidence(
463
+ state.best_confidence, step.confidence
464
+ )
449
465
  state.best_confidence = _max_confidence(
450
466
  state.best_confidence, child_state.best_confidence
451
467
  )
@@ -627,6 +643,34 @@ def _resolve_stop_reason(state: _TraversalState) -> ClassificationStopReason:
627
643
  return ClassificationStopReason.NO_MATCH
628
644
 
629
645
 
646
+ def _should_continue_from_stop(
647
+ step: ClassificationStep,
648
+ resolved_nodes: Sequence[TaxonomyNode],
649
+ threshold: float = _CONTINUE_CONFIDENCE_THRESHOLD,
650
+ ) -> bool:
651
+ """Return True when a stop reason should continue traversal.
652
+
653
+ Parameters
654
+ ----------
655
+ step : ClassificationStep
656
+ Classification step to evaluate.
657
+ resolved_nodes : Sequence[TaxonomyNode]
658
+ Resolved taxonomy nodes from the classification step.
659
+ threshold : float, default=0.7
660
+ Confidence threshold for overriding a stop reason.
661
+
662
+ Returns
663
+ -------
664
+ bool
665
+ True when traversal should proceed despite a stop reason.
666
+ """
667
+ if step.stop_reason is not ClassificationStopReason.STOP:
668
+ return False
669
+ if step.confidence is None or step.confidence < threshold:
670
+ return False
671
+ return any(node.children for node in resolved_nodes)
672
+
673
+
630
674
  def _normalize_roots(
631
675
  taxonomy: TaxonomyNode | Sequence[TaxonomyNode],
632
676
  ) -> list[TaxonomyNode]:
@@ -103,6 +103,9 @@ class ResponseBase(Generic[T]):
103
103
  openai_settings : OpenAISettings or None, default None
104
104
  Fully configured OpenAI settings with API key and default model.
105
105
  Required for normal operation.
106
+ save_messages : bool, default True
107
+ Default behavior for persisting message history on response runs
108
+ and close calls.
106
109
 
107
110
  Attributes
108
111
  ----------
@@ -169,6 +172,7 @@ class ResponseBase(Generic[T]):
169
172
  data_path: Path | str | None = None,
170
173
  tool_handlers: dict[str, ToolHandlerRegistration] | None = None,
171
174
  openai_settings: OpenAISettings | None = None,
175
+ save_messages: bool = True,
172
176
  ) -> None:
173
177
  """Initialize a response session with OpenAI configuration.
174
178
 
@@ -204,6 +208,9 @@ class ResponseBase(Generic[T]):
204
208
  openai_settings : OpenAISettings or None, default None
205
209
  Fully configured OpenAI settings with API key and default model.
206
210
  Required for normal operation.
211
+ save_messages : bool, default True
212
+ Default behavior for persisting message history on response runs
213
+ and close calls.
207
214
 
208
215
  Raises
209
216
  ------
@@ -247,6 +254,7 @@ class ResponseBase(Generic[T]):
247
254
  self._data_path = get_data_path(self.__class__.__name__)
248
255
 
249
256
  self._instructions = instructions
257
+ self._save_messages = save_messages
250
258
  self._tools: list[dict[str, Any]] | None = None
251
259
  if tools is not None:
252
260
  self._tools = [
@@ -314,7 +322,7 @@ class ResponseBase(Generic[T]):
314
322
 
315
323
  self.messages = ResponseMessages()
316
324
  self.messages.add_system_message(content=system_content)
317
- if self._data_path is not None:
325
+ if self._data_path is not None and self._save_messages:
318
326
  self.save()
319
327
 
320
328
  @property
@@ -509,7 +517,6 @@ class ResponseBase(Generic[T]):
509
517
  content: str | list[str],
510
518
  files: str | list[str] | None = None,
511
519
  use_vector_store: bool = False,
512
- save_messages: bool = True,
513
520
  ) -> T | str:
514
521
  """Generate a response asynchronously from the OpenAI API.
515
522
 
@@ -532,9 +539,6 @@ class ResponseBase(Generic[T]):
532
539
  use_vector_store : bool, default False
533
540
  If True, non-image files are uploaded to a vector store
534
541
  for RAG-enabled search instead of inline base64 encoding.
535
- save_messages : bool, default True
536
- When True, persist the message history after each response or
537
- tool call.
538
542
 
539
543
  Returns
540
544
  -------
@@ -625,7 +629,7 @@ class ResponseBase(Generic[T]):
625
629
  self.messages.add_tool_message(
626
630
  content=response_output, output=tool_output
627
631
  )
628
- if save_messages:
632
+ if self._save_messages:
629
633
  self.save()
630
634
  except Exception as exc:
631
635
  log(
@@ -651,7 +655,7 @@ class ResponseBase(Generic[T]):
651
655
  self.messages.add_assistant_message(
652
656
  response_output, metadata=kwargs
653
657
  )
654
- if save_messages:
658
+ if self._save_messages:
655
659
  self.save()
656
660
  if hasattr(response, "output_text") and response.output_text:
657
661
  raw_text = response.output_text
@@ -688,7 +692,6 @@ class ResponseBase(Generic[T]):
688
692
  *,
689
693
  files: str | list[str] | None = None,
690
694
  use_vector_store: bool = False,
691
- save_messages: bool = True,
692
695
  ) -> T | str:
693
696
  """Execute run_async synchronously with proper event loop handling.
694
697
 
@@ -711,9 +714,6 @@ class ResponseBase(Generic[T]):
711
714
  use_vector_store : bool, default False
712
715
  If True, non-image files are uploaded to a vector store
713
716
  for RAG-enabled search instead of inline base64 encoding.
714
- save_messages : bool, default True
715
- When True, persist the message history after each response or
716
- tool call.
717
717
 
718
718
  Returns
719
719
  -------
@@ -749,7 +749,6 @@ class ResponseBase(Generic[T]):
749
749
  content=content,
750
750
  files=files,
751
751
  use_vector_store=use_vector_store,
752
- save_messages=save_messages,
753
752
  )
754
753
 
755
754
  try:
@@ -1003,7 +1002,8 @@ class ResponseBase(Generic[T]):
1003
1002
  ... response.close()
1004
1003
  """
1005
1004
  log(f"Closing session {self.uuid} for {self.__class__.__name__}")
1006
- self.save()
1005
+ if self._save_messages:
1006
+ self.save()
1007
1007
 
1008
1008
  # Clean up tracked Files API uploads
1009
1009
  try:
@@ -102,6 +102,9 @@ class ResponseConfiguration(DataclassJSONSerializable, Generic[TIn, TOut]):
102
102
  system_vector_store : list[str], optional
103
103
  Optional list of vector store names to attach as system context.
104
104
  Default is None.
105
+ save_messages : bool, optional
106
+ Default behavior for persisting message history on response runs
107
+ and close calls. Default is True.
105
108
  add_output_instructions : bool, optional
106
109
  Whether to append output structure instructions to the prompt.
107
110
  Default is False.
@@ -114,6 +117,7 @@ class ResponseConfiguration(DataclassJSONSerializable, Generic[TIn, TOut]):
114
117
  If name is not a non-empty string.
115
118
  If instructions is not a string or Path.
116
119
  If tools is provided and is not a sequence.
120
+ If save_messages is not a bool.
117
121
  If input_structure or output_structure is not a class.
118
122
  If input_structure or output_structure does not subclass StructureBase.
119
123
  ValueError
@@ -158,6 +162,7 @@ class ResponseConfiguration(DataclassJSONSerializable, Generic[TIn, TOut]):
158
162
  input_structure: Optional[Type[TIn]]
159
163
  output_structure: Optional[Type[TOut]]
160
164
  system_vector_store: Optional[list[str]] = None
165
+ save_messages: bool = True
161
166
  add_output_instructions: bool = False
162
167
  add_web_search_tool: bool = False
163
168
 
@@ -204,6 +209,8 @@ class ResponseConfiguration(DataclassJSONSerializable, Generic[TIn, TOut]):
204
209
 
205
210
  if self.tools is not None and not isinstance(self.tools, Sequence):
206
211
  raise TypeError("Configuration.tools must be a Sequence or None")
212
+ if not isinstance(self.save_messages, bool):
213
+ raise TypeError("Configuration.save_messages must be a bool")
207
214
 
208
215
  @property
209
216
  def get_resolved_instructions(self) -> str:
@@ -274,6 +281,7 @@ class ResponseConfiguration(DataclassJSONSerializable, Generic[TIn, TOut]):
274
281
  data_path=data_path,
275
282
  tool_handlers=tool_handlers,
276
283
  openai_settings=openai_settings,
284
+ save_messages=self.save_messages,
277
285
  )
278
286
 
279
287
 
@@ -480,6 +480,8 @@ class ClassificationResult(StructureBase):
480
480
  Yield selected identifiers across all steps.
481
481
  selected_nodes
482
482
  Return the selected identifiers across all steps.
483
+ to_lightweight_summary
484
+ Return a lightweight summary of selected node paths.
483
485
 
484
486
  Examples
485
487
  --------
@@ -565,6 +567,52 @@ class ClassificationResult(StructureBase):
565
567
  if normalized:
566
568
  yield normalized
567
569
 
570
+ def to_lightweight_summary(self) -> "ClassificationSummary | None":
571
+ """Return a lightweight summary of selected node paths.
572
+
573
+ Returns
574
+ -------
575
+ ClassificationSummary or None
576
+ Summary containing the ``full_paths`` of selected nodes, or None
577
+ when no selections exist.
578
+
579
+ Examples
580
+ --------
581
+ >>> result = ClassificationResult(steps=[])
582
+ >>> result.to_lightweight_summary() is None
583
+ True
584
+ """
585
+ full_paths = [
586
+ format_path_identifier(taxonomy_enum_path(node))
587
+ for node in self.selected_nodes
588
+ ]
589
+ if not full_paths:
590
+ return None
591
+ return ClassificationSummary(full_paths=full_paths)
592
+
593
+
594
+ class ClassificationSummary(StructureBase):
595
+ """Represent a lightweight summary of selected taxonomy paths.
596
+
597
+ Attributes
598
+ ----------
599
+ full_paths : list[str] or None
600
+ Selected taxonomy paths including parent segments.
601
+
602
+ Methods
603
+ -------
604
+ to_json()
605
+ Serialize the summary to a JSON-compatible dictionary.
606
+ from_json(data)
607
+ Construct a summary instance from JSON-compatible data.
608
+ """
609
+
610
+ full_paths: list[str] | None = spec_field(
611
+ "full_paths",
612
+ description="Selected taxonomy paths including parent segments.",
613
+ default=None,
614
+ )
615
+
568
616
 
569
617
  def taxonomy_enum_path(value: Enum | str | None) -> list[str]:
570
618
  """Return the taxonomy path segments for an enum value.
@@ -598,6 +646,7 @@ def taxonomy_enum_path(value: Enum | str | None) -> list[str]:
598
646
 
599
647
  __all__ = [
600
648
  "ClassificationResult",
649
+ "ClassificationSummary",
601
650
  "ClassificationStep",
602
651
  "ClassificationStopReason",
603
652
  "Taxonomy",