sofias-sdk-lite 0.2.0__tar.gz → 0.2.1__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 (81) hide show
  1. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/PKG-INFO +1 -1
  2. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/pyproject.toml +1 -1
  3. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/pyproject.toml.orig +1 -1
  4. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/agent/agent.py +3 -1
  5. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/agent/agent_builder.py +6 -0
  6. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/agent/agent_config.py +2 -0
  7. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/agent/middleware.py +3 -1
  8. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/agent/response_workflow.py +7 -1
  9. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/config/agent_settings.py +3 -2
  10. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/config/runtime_config.py +4 -0
  11. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/config/sdk_config.py +2 -0
  12. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/config/source.py +6 -0
  13. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/contracts/agent_contracts.py +2 -0
  14. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/contracts/base_contracts.py +2 -0
  15. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/errors/error_handler.py +2 -0
  16. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/messaging/models.py +6 -0
  17. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/nodes/aggregator_config.py +2 -0
  18. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/nodes/aggregator_node.py +2 -0
  19. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/nodes/base_node.py +2 -0
  20. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/nodes/delegation_config.py +6 -0
  21. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/nodes/function_node.py +2 -0
  22. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/nodes/llm_node.py +2 -0
  23. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/nodes/llm_node_config.py +4 -0
  24. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/nodes/planner_node.py +2 -0
  25. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/nodes/prompt_assembler.py +2 -0
  26. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/observability/events.py +2 -0
  27. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/observability/tracing.py +4 -0
  28. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/rabbitmq/consumer.py +3 -1
  29. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/rabbitmq/delegation_transport.py +2 -0
  30. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/rabbitmq/rpc_client.py +2 -0
  31. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/routing/router.py +2 -0
  32. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/routing/strategies.py +14 -0
  33. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/runner/runner.py +3 -1
  34. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/state/conversation_state.py +4 -0
  35. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/state/history.py +2 -0
  36. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/LICENSE +0 -0
  37. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/README.md +0 -0
  38. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/__init__.py +0 -0
  39. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/agent/__init__.py +0 -0
  40. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/agent/execution_context.py +0 -0
  41. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/agent/streaming.py +0 -0
  42. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/config/__init__.py +0 -0
  43. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/config/defaults.py +0 -0
  44. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/contracts/__init__.py +0 -0
  45. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/contracts/node_contracts.py +0 -0
  46. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/contracts/strict_contract.py +0 -0
  47. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/errors/__init__.py +0 -0
  48. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/errors/circuit_breaker.py +0 -0
  49. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/errors/exceptions.py +0 -0
  50. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/llm/__init__.py +0 -0
  51. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/llm/factory.py +8 -8
  52. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/llm/gateway.py +0 -0
  53. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/llm/openai_compatible.py +0 -0
  54. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/llm/protocol.py +0 -0
  55. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/llm/tokens.py +0 -0
  56. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/llm/tools.py +0 -0
  57. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/messaging/__init__.py +0 -0
  58. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/nodes/__init__.py +0 -0
  59. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/nodes/delegation_node.py +0 -0
  60. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/nodes/delegation_transport.py +0 -0
  61. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/nodes/plan_executor.py +0 -0
  62. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/nodes/planning_models.py +0 -0
  63. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/observability/__init__.py +0 -0
  64. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/observability/_log.py +0 -0
  65. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/observability/trace_context.py +0 -0
  66. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/py.typed +0 -0
  67. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/rabbitmq/__init__.py +0 -0
  68. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/rabbitmq/client.py +0 -0
  69. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/rabbitmq/config.py +0 -0
  70. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/rabbitmq/publisher.py +0 -0
  71. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/rabbitmq/types.py +0 -0
  72. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/routing/__init__.py +0 -0
  73. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/runner/__init__.py +0 -0
  74. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/runner/config.py +0 -0
  75. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/state/__init__.py +0 -0
  76. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/state/memory.py +0 -0
  77. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/tools.py +0 -0
  78. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/workflows/__init__.py +0 -0
  79. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/workflows/chat.py +0 -0
  80. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/workflows/null.py +0 -0
  81. {sofias_sdk_lite-0.2.0 → sofias_sdk_lite-0.2.1}/src/sofias_sdk_lite/workflows/streaming_chat.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sofias-sdk-lite
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Build agents on the Sofias agent graph and run them over RabbitMQ.
5
5
  Keywords: agents,ai,llm,rabbitmq,sofias
6
6
  Author: Sofias Tech, S.L.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sofias-sdk-lite"
3
- version = "0.2.0"
3
+ version = "0.2.1"
4
4
  description = "Build agents on the Sofias agent graph and run them over RabbitMQ."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sofias-sdk-lite"
3
- version = "0.2.0"
3
+ version = "0.2.1"
4
4
  description = "Build agents on the Sofias agent graph and run them over RabbitMQ."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -67,12 +67,13 @@ class Agent:
67
67
  to Node instances which handle their own execution.
68
68
 
69
69
  Example:
70
+ ```python
70
71
  # Agent is typically built via AgentBuilder
71
72
  agent = (
72
73
  AgentBuilder("my_agent", version="1.0.0")
73
74
  .with_settings_class(MySettings)
74
75
  .with_contract(input_schema=MyInput, output_schema=MyOutput)
75
- .add_node("start", node_config, contract)
76
+ .add_llm_node("start", node_config, contract)
76
77
  .set_entry_node("start")
77
78
  .set_terminal("start")
78
79
  .build()
@@ -86,6 +87,7 @@ class Agent:
86
87
  )
87
88
  )
88
89
  assert response.status == ResponseStatus.SUCCESS
90
+ ```
89
91
  """
90
92
 
91
93
  def __init__(
@@ -64,6 +64,7 @@ class AgentBuilder:
64
64
  time, catching configuration errors before runtime.
65
65
 
66
66
  Example:
67
+ ```python
67
68
  agent = (
68
69
  AgentBuilder("inbox_assist", version="1.0.0")
69
70
  .with_description("Agent that processes incoming emails")
@@ -84,6 +85,7 @@ class AgentBuilder:
84
85
  .with_circuit_breaker(CircuitBreakerConfig(failure_threshold=5))
85
86
  .build()
86
87
  )
88
+ ```
87
89
  """
88
90
 
89
91
  def __init__(self, name: str, *, version: str = "1.0.0") -> None:
@@ -408,6 +410,7 @@ class AgentBuilder:
408
410
  Self for method chaining.
409
411
 
410
412
  Example:
413
+ ```python
411
414
  builder.with_delegation_transport(my_transport).add_delegation_node(
412
415
  name="delegate_analysis",
413
416
  config=DelegationNodeConfig(
@@ -424,6 +427,7 @@ class AgentBuilder:
424
427
  "transform_for_analyzer": lambda d: {"text": d["content"]},
425
428
  },
426
429
  )
430
+ ```
427
431
  """
428
432
  self._delegation_node_configs[name] = config
429
433
  self._delegation_node_contracts[name] = contract
@@ -709,6 +713,7 @@ class AgentBuilder:
709
713
  Self for method chaining.
710
714
 
711
715
  Example:
716
+ ```python
712
717
  from sofias_sdk_lite.state import (
713
718
  ConversationState,
714
719
  InMemoryStateProvider,
@@ -717,6 +722,7 @@ class AgentBuilder:
717
722
  builder.with_conversation_state(
718
723
  ConversationState(InMemoryStateProvider())
719
724
  )
725
+ ```
720
726
  """
721
727
  self._conversation_state = state
722
728
  return self
@@ -47,6 +47,7 @@ class AgentConfig(BaseModel):
47
47
  three-level configuration resolution: SDK > Agent > Node.
48
48
 
49
49
  Example:
50
+ ```python
50
51
  config = AgentConfig(
51
52
  name="inbox-assistant",
52
53
  version="1.0.0",
@@ -56,6 +57,7 @@ class AgentConfig(BaseModel):
56
57
  settings_class=InboxAssistantSettings,
57
58
  llm=AgentLLMConfig(model="gpt-4o"),
58
59
  )
60
+ ```
59
61
  """
60
62
 
61
63
  model_config = ConfigDict(frozen=True, arbitrary_types_allowed=True)
@@ -4,7 +4,8 @@ Middleware hooks are called before and after each node execution,
4
4
  allowing logging, metrics, tracing, and input/output transformation
5
5
  without modifying node code.
6
6
 
7
- Example::
7
+ Example:
8
+ ```python
8
9
 
9
10
  class MetricsMiddleware:
10
11
  async def before_node(self, node_name, input_data):
@@ -24,6 +25,7 @@ Example::
24
25
  .with_middleware(MetricsMiddleware())
25
26
  .build()
26
27
  )
28
+ ```
27
29
  """
28
30
 
29
31
  from __future__ import annotations
@@ -21,6 +21,7 @@ This module is transport-agnostic: it has no dependency on any specific
21
21
  message broker. Wire it to whatever transport your application uses.
22
22
 
23
23
  Example:
24
+ ```python
24
25
  class MyRabbitMQWorkflow:
25
26
  async def send_response(
26
27
  self,
@@ -38,6 +39,7 @@ Example:
38
39
  .with_response_workflow(MyRabbitMQWorkflow())
39
40
  .build()
40
41
  )
42
+ ```
41
43
  """
42
44
 
43
45
  from __future__ import annotations
@@ -99,6 +101,7 @@ class StreamingResponseWorkflow(Protocol):
99
101
  send_response() is still called at the end with the complete response.
100
102
 
101
103
  Example:
104
+ ```python
102
105
  class MySSEWorkflow:
103
106
  async def on_stream_event(
104
107
  self,
@@ -113,6 +116,7 @@ class StreamingResponseWorkflow(Protocol):
113
116
  context: ExecutionContext,
114
117
  ) -> None:
115
118
  await self._sse_channel.send_final(response.model_dump_json())
119
+ ```
116
120
  """
117
121
 
118
122
  async def on_stream_event(
@@ -162,7 +166,8 @@ class BaseDelegationResponseWorkflow:
162
166
  the ``result`` string. If *None*, the entire content dict is
163
167
  serialized as JSON. Defaults to *None*.
164
168
 
165
- Example::
169
+ Example:
170
+ ```python
166
171
 
167
172
  async def publish(payload: dict) -> None:
168
173
  await my_transport.publish_reply(reply_to, correlation_id, payload)
@@ -171,6 +176,7 @@ class BaseDelegationResponseWorkflow:
171
176
  publish_fn=publish,
172
177
  output_field="report",
173
178
  )
179
+ ```
174
180
  """
175
181
 
176
182
  def __init__(
@@ -55,12 +55,13 @@ class BaseAgentSettings(AgentSettings):
55
55
  variables), so a ``{api_key}`` placeholder can never inline a secret. Read
56
56
  them at the point of use with ``.get_secret_value()``.
57
57
 
58
- Example::
59
-
58
+ Example:
59
+ ```python
60
60
  class MySettings(BaseAgentSettings):
61
61
  CREDENTIAL_FIELDS = BaseAgentSettings.CREDENTIAL_FIELDS | {"search_api_key"}
62
62
 
63
63
  search_api_key: SecretStr = SecretStr("")
64
+ ```
64
65
  """
65
66
 
66
67
  # --- LLM gateway (canonical platform keys) ---
@@ -38,11 +38,13 @@ class AgentSettings(BaseModel):
38
38
  required and optional runtime configuration variables.
39
39
 
40
40
  Example:
41
+ ```python
41
42
  class MyAgentSettings(AgentSettings):
42
43
  reasoning_model: str # required
43
44
  fast_model: str # required
44
45
  max_retries: int = 3 # optional with default
45
46
  temperature: float = 0.7 # optional with default
47
+ ```
46
48
  """
47
49
 
48
50
  model_config = ConfigDict(
@@ -85,6 +87,7 @@ class SettingsResolver(Generic[SettingsT]):
85
87
  graph execution without explicit parameter passing.
86
88
 
87
89
  Example:
90
+ ```python
88
91
  resolver = SettingsResolver(MyAgentSettings)
89
92
 
90
93
  # Resolve from incoming message
@@ -98,6 +101,7 @@ class SettingsResolver(Generic[SettingsT]):
98
101
 
99
102
  # Clear after execution
100
103
  resolver.clear()
104
+ ```
101
105
  """
102
106
 
103
107
  def __init__(self, settings_class: type[SettingsT]) -> None:
@@ -73,10 +73,12 @@ class SDKConfig(BaseModel):
73
73
  when setting up their agent. All values have sensible defaults.
74
74
 
75
75
  Example:
76
+ ```python
76
77
  config = SDKConfig(
77
78
  llm=LLMConfig(model="gpt-4o-mini"),
78
79
  strict_validation=True,
79
80
  )
81
+ ```
80
82
  """
81
83
 
82
84
  model_config = ConfigDict(frozen=True)
@@ -35,11 +35,13 @@ class ConfigSource(Protocol):
35
35
  return type.
36
36
 
37
37
  Example:
38
+ ```python
38
39
  class DatabaseConfigSource:
39
40
  async def fetch(
40
41
  self, agent_name: str, conversation_id: str | None = None
41
42
  ) -> dict[str, Any]:
42
43
  return await my_db.get_agent_config(agent_name)
44
+ ```
43
45
  """
44
46
 
45
47
  async def fetch(self, agent_name: str, conversation_id: str | None = None) -> dict[str, Any]:
@@ -66,8 +68,10 @@ class StaticConfigSource:
66
68
  fixed configuration applies to every agent and conversation.
67
69
 
68
70
  Example:
71
+ ```python
69
72
  source = StaticConfigSource({"model_name": "gpt-4o-mini", "api_key": "..."})
70
73
  config = await source.fetch("my_agent")
74
+ ```
71
75
  """
72
76
 
73
77
  def __init__(self, config: dict[str, Any]) -> None:
@@ -104,8 +108,10 @@ class EnvConfigSource:
104
108
  export SOFIAS_AGENT_CONFIG='{"model_name": "gpt-4o-mini", "api_key": "sk-..."}'
105
109
 
106
110
  Example:
111
+ ```python
107
112
  source = EnvConfigSource() # reads SOFIAS_AGENT_CONFIG
108
113
  config = await source.fetch("my_agent")
114
+ ```
109
115
  """
110
116
 
111
117
  def __init__(self, env_var: str = "SOFIAS_AGENT_CONFIG") -> None:
@@ -8,6 +8,7 @@ strongly-typed envelopes that carry domain data plus transport metadata.
8
8
  - AgentResponse wraps the validated output with execution metadata.
9
9
 
10
10
  Example:
11
+ ```python
11
12
  class MyInput(InputContract):
12
13
  query: str
13
14
 
@@ -22,6 +23,7 @@ Example:
22
23
  print(response.content) # validated output dict
23
24
  print(response.execution_path) # ["classifier", "responder"]
24
25
  print(response.execution_time_ms) # 1234.5
26
+ ```
25
27
  """
26
28
 
27
29
  from __future__ import annotations
@@ -6,6 +6,7 @@ a common protocol so that any delegating node knows exactly what to send and
6
6
  what to expect back, without needing to know the internals of the target agent.
7
7
 
8
8
  Example:
9
+ ```python
9
10
  # Agent A delegates to Agent B
10
11
  request = DelegationRequest(
11
12
  input="Summarize this document",
@@ -18,6 +19,7 @@ Example:
18
19
  result="The document discusses...",
19
20
  extras={"word_count": 150, "confidence": 0.95},
20
21
  )
22
+ ```
21
23
  """
22
24
 
23
25
  from __future__ import annotations
@@ -153,12 +153,14 @@ class ErrorHandler:
153
153
  callable provided by the agent.
154
154
 
155
155
  Example:
156
+ ```python
156
157
  handler = ErrorHandler(config=config, store=store)
157
158
  result = await handler.handle_node_execution(
158
159
  node_name="processor",
159
160
  execute_fn=node.execute,
160
161
  input_data={"query": "hello"},
161
162
  )
163
+ ```
162
164
  """
163
165
 
164
166
  def __init__(
@@ -332,6 +332,7 @@ class DelegationTarget(BaseModel):
332
332
  the communication with a specific target agent.
333
333
 
334
334
  Example:
335
+ ```python
335
336
  target = DelegationTarget(
336
337
  agent_name="summarizer",
337
338
  routing_key="agents.summarizer.tasks",
@@ -339,6 +340,7 @@ class DelegationTarget(BaseModel):
339
340
  output_contract=SummaryOutput,
340
341
  timeout_seconds=120,
341
342
  )
343
+ ```
342
344
 
343
345
  .. note::
344
346
  Unlike the internal platform, this SDK does **not** assume any
@@ -394,12 +396,14 @@ class DelegationStep(BaseModel):
394
396
  on other steps. Used when dynamic targets are enabled.
395
397
 
396
398
  Example:
399
+ ```python
397
400
  step = DelegationStep(
398
401
  id="step_1",
399
402
  agent_name="web-search",
400
403
  input={"query": "Bitcoin price"},
401
404
  depends_on=[],
402
405
  )
406
+ ```
403
407
  """
404
408
 
405
409
  model_config = {"frozen": True}
@@ -445,6 +449,7 @@ class DelegationPlan(BaseModel):
445
449
  delegation node running in DAG mode with dynamic targets.
446
450
 
447
451
  Example:
452
+ ```python
448
453
  plan = DelegationPlan(
449
454
  steps=[
450
455
  DelegationStep(id="s1", agent_name="search", input={"q": "A"}),
@@ -452,6 +457,7 @@ class DelegationPlan(BaseModel):
452
457
  DelegationStep(id="s3", agent_name="synthesizer", input={}, depends_on=["s1", "s2"]),
453
458
  ]
454
459
  )
460
+ ```
455
461
  """
456
462
 
457
463
  model_config = {"frozen": True}
@@ -19,6 +19,7 @@ class AggregatorNodeConfig(BaseModel):
19
19
  to a configurable policy (all, any, majority).
20
20
 
21
21
  Example:
22
+ ```python
22
23
  config = AggregatorNodeConfig(
23
24
  name="collect_results",
24
25
  description="Collects responses from all sub-agents",
@@ -26,6 +27,7 @@ class AggregatorNodeConfig(BaseModel):
26
27
  timeout_seconds=120.0,
27
28
  on_timeout="partial_result",
28
29
  )
30
+ ```
29
31
  """
30
32
 
31
33
  model_config = ConfigDict(frozen=True)
@@ -53,6 +53,7 @@ class AggregatorNode(BaseNode):
53
53
  controlled by retry_timeout_seconds (or timeout_seconds if not set).
54
54
 
55
55
  Example:
56
+ ```python
56
57
  config = AggregatorNodeConfig(
57
58
  name="collect_results",
58
59
  resolution_policy="all",
@@ -73,6 +74,7 @@ class AggregatorNode(BaseNode):
73
74
  # "total_expected": 2,
74
75
  # "total_received": 2,
75
76
  # }
77
+ ```
76
78
  """
77
79
 
78
80
  def __init__(
@@ -50,10 +50,12 @@ class BaseNode(ABC):
50
50
  - FunctionNode: Nodes that execute deterministic Python logic.
51
51
 
52
52
  Example:
53
+ ```python
53
54
  class MyCustomNode(BaseNode):
54
55
  async def _run(self, input_data: dict, context: dict | None = None) -> dict:
55
56
  # Custom logic here
56
57
  return {"result": "processed"}
58
+ ```
57
59
  """
58
60
 
59
61
  def __init__(
@@ -26,6 +26,7 @@ class DelegationTarget(BaseModel):
26
26
  the communication with a specific target agent.
27
27
 
28
28
  Example:
29
+ ```python
29
30
  target = DelegationTarget(
30
31
  agent_name="summarizer",
31
32
  routing_key="agent.summarizer",
@@ -33,6 +34,7 @@ class DelegationTarget(BaseModel):
33
34
  output_contract=SummaryOutput,
34
35
  timeout_seconds=120,
35
36
  )
37
+ ```
36
38
  """
37
39
 
38
40
  model_config = ConfigDict(frozen=True)
@@ -69,12 +71,14 @@ class DelegationStep(BaseModel):
69
71
  on other steps. Used when dynamic_targets is enabled.
70
72
 
71
73
  Example:
74
+ ```python
72
75
  step = DelegationStep(
73
76
  id="step_1",
74
77
  agent_name="web-search",
75
78
  input={"query": "Bitcoin price"},
76
79
  depends_on=[],
77
80
  )
81
+ ```
78
82
  """
79
83
 
80
84
  model_config = ConfigDict(frozen=True)
@@ -107,6 +111,7 @@ class DelegationPlan(BaseModel):
107
111
  when execution_mode='dag' and dynamic_targets=True.
108
112
 
109
113
  Example:
114
+ ```python
110
115
  plan = DelegationPlan(
111
116
  steps=[
112
117
  DelegationStep(id="s1", agent_name="search", input={"q": "A"}),
@@ -114,6 +119,7 @@ class DelegationPlan(BaseModel):
114
119
  DelegationStep(id="s3", agent_name="synthesizer", input={}, depends_on=["s1", "s2"]),
115
120
  ]
116
121
  )
122
+ ```
117
123
  """
118
124
 
119
125
  model_config = ConfigDict(frozen=True)
@@ -63,6 +63,7 @@ class FunctionNode(BaseNode):
63
63
  return {"merged": ...}
64
64
 
65
65
  Example:
66
+ ```python
66
67
  node = FunctionNode(
67
68
  name="transformer",
68
69
  contract=NodeContract(input_schema=RawInput, output_schema=FormattedOutput),
@@ -70,6 +71,7 @@ class FunctionNode(BaseNode):
70
71
  )
71
72
  result = await node.execute({"input": 5})
72
73
  # result: {"value": 10}
74
+ ```
73
75
  """
74
76
 
75
77
  def __init__(
@@ -133,6 +133,7 @@ class LLMNode(BaseNode):
133
133
  result.
134
134
 
135
135
  Example:
136
+ ```python
136
137
  node = LLMNode(
137
138
  config=node_config,
138
139
  contract=NodeContract(input_schema=MyInput, output_schema=MyOutput),
@@ -140,6 +141,7 @@ class LLMNode(BaseNode):
140
141
  tools=[tool1, tool2],
141
142
  )
142
143
  result = await node.execute({"query": "Hello"})
144
+ ```
143
145
  """
144
146
 
145
147
  def __init__(
@@ -157,6 +157,7 @@ class LLMNodeConfig(BaseModel):
157
157
  are optional and default to None, meaning "inherit from parent level".
158
158
 
159
159
  Example:
160
+ ```python
160
161
  config = LLMNodeConfig(
161
162
  name="classifier",
162
163
  description="Classifies incoming emails",
@@ -168,6 +169,7 @@ class LLMNodeConfig(BaseModel):
168
169
  template_variables={"categories": ["spam", "urgent", "normal"]},
169
170
  ),
170
171
  )
172
+ ```
171
173
  """
172
174
 
173
175
  model_config = ConfigDict(frozen=True)
@@ -256,11 +258,13 @@ class FunctionNodeConfig(BaseModel):
256
258
  without an LLM. No prompt, tool loop, or LLM settings are needed.
257
259
 
258
260
  Example:
261
+ ```python
259
262
  config = FunctionNodeConfig(
260
263
  name="data_merger",
261
264
  description="Merges outputs from multiple upstream nodes",
262
265
  retry_policy=RetryPolicy(max_retries=2),
263
266
  )
267
+ ```
264
268
  """
265
269
 
266
270
  model_config = ConfigDict(frozen=True)
@@ -38,6 +38,7 @@ class PlannerNode(BaseNode):
38
38
  routes the output to PlanExecutor for execution.
39
39
 
40
40
  Example:
41
+ ```python
41
42
  planner = PlannerNode(
42
43
  name="planner",
43
44
  contract=planner_contract,
@@ -47,6 +48,7 @@ class PlannerNode(BaseNode):
47
48
  NodeDescriptor(name="summarize", node_type=NodeType.LLM, description="Summarize text"),
48
49
  ],
49
50
  )
51
+ ```
50
52
  """
51
53
 
52
54
  def __init__(
@@ -33,12 +33,14 @@ class PromptAssembler:
33
33
  variables merged with runtime variables (runtime takes precedence).
34
34
 
35
35
  Example:
36
+ ```python
36
37
  assembler = PromptAssembler(
37
38
  config=node_config,
38
39
  tools=[tool_spec_1, tool_spec_2],
39
40
  output_contract=MyOutputContract,
40
41
  )
41
42
  prompt = assembler.assemble(runtime_vars={"user_name": "Alice"})
43
+ ```
42
44
  """
43
45
 
44
46
  def __init__(
@@ -134,6 +134,7 @@ class EventAdapter:
134
134
  - Error handling and logging
135
135
 
136
136
  Example:
137
+ ```python
137
138
  publisher = RabbitMQPublisher(connection)
138
139
  listener = RabbitMQListener(connection)
139
140
  events = EventAdapter(publisher, listener)
@@ -151,6 +152,7 @@ class EventAdapter:
151
152
  agent_name="inbox_assist",
152
153
  handler=my_handler,
153
154
  )
155
+ ```
154
156
  """
155
157
 
156
158
  def __init__(
@@ -215,6 +215,7 @@ class TracingAdapter:
215
215
  - No-op behavior when provider is None
216
216
 
217
217
  Example:
218
+ ```python
218
219
  provider = OTelTracingProvider("my-agent-service")
219
220
  tracing = TracingAdapter(provider)
220
221
 
@@ -226,6 +227,7 @@ class TracingAdapter:
226
227
  # Trace node execution
227
228
  with tracing.trace_node_execution("classifier", input_data) as span:
228
229
  result = await node.execute(input_data)
230
+ ```
229
231
  """
230
232
 
231
233
  def __init__(self, provider: TracingProvider | None = None) -> None:
@@ -298,9 +300,11 @@ class TracingAdapter:
298
300
  The span for adding events and attributes.
299
301
 
300
302
  Example:
303
+ ```python
301
304
  with tracing.trace_agent_execution("my_agent", "1.0.0") as span:
302
305
  span.set_attribute("input.type", "email")
303
306
  response = agent.execute(message)
307
+ ```
304
308
  """
305
309
  attributes = {
306
310
  "agent.name": agent_name,
@@ -27,7 +27,8 @@ class RabbitMQConsumer:
27
27
  The handler receives a :class:`MessageContext` with the deserialized
28
28
  payload, AMQP metadata, and ack/nack helpers.
29
29
 
30
- Example::
30
+ Example:
31
+ ```python
31
32
 
32
33
  async with RabbitMQClient(config) as client:
33
34
  consumer = RabbitMQConsumer(
@@ -40,6 +41,7 @@ class RabbitMQConsumer:
40
41
  await asyncio.Event().wait()
41
42
  except asyncio.CancelledError:
42
43
  await consumer.stop()
44
+ ```
43
45
  """
44
46
 
45
47
  def __init__(
@@ -38,6 +38,7 @@ class RabbitMQDelegationTransport:
38
38
  - Responses arriving before wait_* are buffered, not lost
39
39
 
40
40
  Example:
41
+ ```python
41
42
  client = RabbitMQClient(config)
42
43
  await client.connect()
43
44
 
@@ -65,6 +66,7 @@ class RabbitMQDelegationTransport:
65
66
  print(f"{cid} responded with: {response}")
66
67
 
67
68
  await client.disconnect()
69
+ ```
68
70
  """
69
71
 
70
72
  def __init__(
@@ -38,6 +38,7 @@ class RabbitMQRPCClient:
38
38
  3. Waits for responses matching the correlation_id
39
39
 
40
40
  Example:
41
+ ```python
41
42
  client = RabbitMQClient(config)
42
43
  await client.connect()
43
44
 
@@ -49,6 +50,7 @@ class RabbitMQRPCClient:
49
50
  )
50
51
 
51
52
  await client.disconnect()
53
+ ```
52
54
  """
53
55
 
54
56
  def __init__(
@@ -31,6 +31,7 @@ class Router:
31
31
  graph. Execution is handled by the agent (Phase 5).
32
32
 
33
33
  Example:
34
+ ```python
34
35
  router = Router()
35
36
  router.add_route(
36
37
  "classifier",
@@ -47,6 +48,7 @@ class Router:
47
48
  # Later, during execution:
48
49
  next_node = router.resolve("classifier", {"category": "urgent"})
49
50
  # Returns "priority_handler"
51
+ ```
50
52
  """
51
53
 
52
54
  def __init__(self) -> None:
@@ -57,6 +57,7 @@ class FieldValueStrategy:
57
57
  outputs where different categories lead to different processing paths.
58
58
 
59
59
  Example:
60
+ ```python
60
61
  strategy = FieldValueStrategy(
61
62
  field="category",
62
63
  mapping={"urgent": "priority_handler", "spam": "discard"},
@@ -65,6 +66,7 @@ class FieldValueStrategy:
65
66
  # If output["category"] == "urgent" -> "priority_handler"
66
67
  # If output["category"] == "spam" -> "discard"
67
68
  # If output["category"] == "other" -> "normal_handler" (default)
69
+ ```
68
70
  """
69
71
 
70
72
  def __init__(
@@ -127,6 +129,7 @@ class ConfidenceThresholdStrategy:
127
129
  leads to automated processing and low confidence requires human review.
128
130
 
129
131
  Example:
132
+ ```python
130
133
  strategy = ConfidenceThresholdStrategy(
131
134
  field="confidence",
132
135
  threshold=0.8,
@@ -135,6 +138,7 @@ class ConfidenceThresholdStrategy:
135
138
  )
136
139
  # If output["confidence"] >= 0.8 -> "auto_respond"
137
140
  # If output["confidence"] < 0.8 -> "human_review"
141
+ ```
138
142
  """
139
143
 
140
144
  def __init__(
@@ -199,6 +203,7 @@ class FieldPresenceStrategy:
199
203
  "present" if it exists, is not None, and is not empty (for collections).
200
204
 
201
205
  Example:
206
+ ```python
202
207
  strategy = FieldPresenceStrategy(
203
208
  field="tool_calls",
204
209
  present="tool_executor",
@@ -206,6 +211,7 @@ class FieldPresenceStrategy:
206
211
  )
207
212
  # If output["tool_calls"] has content -> "tool_executor"
208
213
  # If output["tool_calls"] is None/empty -> "final_response"
214
+ ```
209
215
  """
210
216
 
211
217
  def __init__(
@@ -270,12 +276,14 @@ class ConditionalStrategy:
270
276
  and returns the target node name.
271
277
 
272
278
  Example:
279
+ ```python
273
280
  def custom_router(output: dict) -> str:
274
281
  if output["score"] > 0.9 and output["verified"]:
275
282
  return "premium_handler"
276
283
  return "standard_handler"
277
284
 
278
285
  strategy = ConditionalStrategy(condition=custom_router)
286
+ ```
279
287
  """
280
288
 
281
289
  def __init__(
@@ -325,12 +333,14 @@ class FanOutStrategy:
325
333
  collects the results after all parallel branches complete.
326
334
 
327
335
  Example:
336
+ ```python
328
337
  strategy = FanOutStrategy(
329
338
  targets=["sentiment", "entities", "summary"],
330
339
  join_node="merger",
331
340
  )
332
341
  # Dispatches to all three nodes in parallel.
333
342
  # After all complete, execution continues at "merger".
343
+ ```
334
344
  """
335
345
 
336
346
  def __init__(
@@ -402,6 +412,7 @@ class CompositeStrategy:
402
412
  default.
403
413
 
404
414
  Example:
415
+ ```python
405
416
  strategy = CompositeStrategy(
406
417
  strategies=[
407
418
  FieldPresenceStrategy("error", "error_handler", None),
@@ -409,6 +420,7 @@ class CompositeStrategy:
409
420
  ],
410
421
  default="fallback",
411
422
  )
423
+ ```
412
424
  """
413
425
 
414
426
  def __init__(
@@ -455,8 +467,10 @@ class StaticRoute:
455
467
  For conditional routing, use FieldValueStrategy or ConditionalStrategy.
456
468
 
457
469
  Example:
470
+ ```python
458
471
  strategy = StaticRoute("responder")
459
472
  # output is ignored — always returns "responder"
473
+ ```
460
474
  """
461
475
 
462
476
  def __init__(self, target: str) -> None:
@@ -4,7 +4,8 @@ Subclass and implement:
4
4
  - build_agent(settings, workflow) -> Agent
5
5
  - prepare_input(task, history, role) -> AgentMessage
6
6
 
7
- Example::
7
+ Example:
8
+ ```python
8
9
 
9
10
  class MyAgentRunner(AgentRunner):
10
11
  settings_class = MySettings
@@ -30,6 +31,7 @@ Example::
30
31
  agent_name="my_agent",
31
32
  rabbitmq=RabbitMQConfig(host="localhost"),
32
33
  )).run()
34
+ ```
33
35
  """
34
36
 
35
37
  from __future__ import annotations
@@ -105,12 +105,14 @@ class ConversationStateAdapter:
105
105
  (``raise_on_error=False``).
106
106
 
107
107
  Example:
108
+ ```python
108
109
  provider = InMemoryStateProvider()
109
110
  state = ConversationStateAdapter(provider)
110
111
 
111
112
  await state.set("conv-123", "validation:doc1", {"questions": [...]})
112
113
  data = await state.get("conv-123", "validation:doc1")
113
114
  await state.delete("conv-123", "validation:doc1")
115
+ ```
114
116
  """
115
117
 
116
118
  def __init__(
@@ -221,11 +223,13 @@ class InMemoryStateProvider:
221
223
  (state is lost on process restart).
222
224
 
223
225
  Example:
226
+ ```python
224
227
  provider = InMemoryStateProvider()
225
228
  await provider.set("conv-1", "key-a", {"foo": "bar"})
226
229
  data = await provider.get("conv-1", "key-a") # {"foo": "bar"}
227
230
  await provider.delete("conv-1", "key-a")
228
231
  data = await provider.get("conv-1", "key-a") # None
232
+ ```
229
233
  """
230
234
 
231
235
  def __init__(self) -> None:
@@ -114,9 +114,11 @@ class InMemoryHistory:
114
114
  (history is lost on process restart).
115
115
 
116
116
  Example:
117
+ ```python
117
118
  history = InMemoryHistory()
118
119
  await history.append("conv-1", Message(role="user", content="hi"))
119
120
  messages = await history.get("conv-1") # [Message(role="user", ...)]
121
+ ```
120
122
  """
121
123
 
122
124
  def __init__(self) -> None:
File without changes
@@ -232,6 +232,14 @@ def create_llm(
232
232
  "reasoning_effort": reasoning_effort,
233
233
  },
234
234
  )
235
+ if not config.base_url.strip():
236
+ raise LLMConfigurationError(
237
+ f"No LLM gateway URL configured for model '{config.model}'. On the Sofias "
238
+ f"platform {ENV_LLM_BASE_URL} is injected automatically; to run locally, "
239
+ "export it or install a fake LLM with default_llm(...).",
240
+ missing=["base_url"],
241
+ )
242
+
235
243
  if not config.api_key.get_secret_value():
236
244
  logger.warning(
237
245
  "LLM gateway configured without an API key; requests will be unauthenticated",
@@ -251,14 +259,6 @@ def create_llm(
251
259
  if config.temperature is not None:
252
260
  common["temperature"] = config.temperature
253
261
 
254
- if not config.base_url.strip():
255
- raise LLMConfigurationError(
256
- f"No LLM gateway URL configured for model '{config.model}'. On the Sofias "
257
- f"platform {ENV_LLM_BASE_URL} is injected automatically; to run locally, "
258
- "export it or install a fake LLM with default_llm(...).",
259
- missing=["base_url"],
260
- )
261
-
262
262
  provider_name = config.provider.lower()
263
263
  if provider_name == PROVIDER_GATEWAY:
264
264
  return GatewayLLM(reasoning_effort=config.reasoning_effort, **common)