simple-agents-py 0.1.11__tar.gz → 0.1.14__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 (135) hide show
  1. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/Cargo.lock +18 -11
  2. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/Cargo.toml +1 -1
  3. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/PKG-INFO +116 -1
  4. {simple_agents_py-0.1.11/crates/simple-agents-py → simple_agents_py-0.1.14}/README.md +115 -0
  5. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agent-type/src/lib.rs +7 -0
  6. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agent-type/src/message.rs +20 -0
  7. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agent-type/src/provider.rs +50 -5
  8. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agent-type/src/request.rs +23 -0
  9. simple_agents_py-0.1.14/crates/simple-agent-type/src/tool.rs +93 -0
  10. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-core/Cargo.toml +4 -3
  11. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-core/src/client.rs +24 -2
  12. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-core/src/routing.rs +22 -1
  13. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-healing/Cargo.toml +1 -1
  14. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/Cargo.toml +3 -3
  15. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/src/anthropic/mod.rs +7 -0
  16. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/src/openai/mod.rs +2 -0
  17. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/src/openai/models.rs +10 -0
  18. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/src/openrouter/mod.rs +2 -0
  19. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-py/Cargo.toml +7 -2
  20. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14/crates/simple-agents-py}/README.md +115 -0
  21. simple_agents_py-0.1.14/crates/simple-agents-py/examples/client_builder_demo.py +298 -0
  22. simple_agents_py-0.1.14/crates/simple-agents-py/examples/direct_healing_demo.py +73 -0
  23. simple_agents_py-0.1.14/crates/simple-agents-py/examples/healing_demo.py +48 -0
  24. simple_agents_py-0.1.14/crates/simple-agents-py/examples/routing_config_demo.py +469 -0
  25. simple_agents_py-0.1.14/crates/simple-agents-py/examples/streaming_demo.py +121 -0
  26. simple_agents_py-0.1.14/crates/simple-agents-py/examples/streaming_parser_demo.py +192 -0
  27. simple_agents_py-0.1.14/crates/simple-agents-py/examples/structured_streaming_demo.py +244 -0
  28. simple_agents_py-0.1.14/crates/simple-agents-py/src/lib.rs +2494 -0
  29. simple_agents_py-0.1.14/crates/simple-agents-py/tests/test_client_builder.py +312 -0
  30. simple_agents_py-0.1.14/crates/simple-agents-py/tests/test_direct_healing.py +109 -0
  31. simple_agents_py-0.1.14/crates/simple-agents-py/tests/test_healing.py +79 -0
  32. simple_agents_py-0.1.14/crates/simple-agents-py/tests/test_integration_openai.py +78 -0
  33. simple_agents_py-0.1.14/crates/simple-agents-py/tests/test_routing_config.py +402 -0
  34. simple_agents_py-0.1.14/crates/simple-agents-py/tests/test_streaming.py +118 -0
  35. simple_agents_py-0.1.14/crates/simple-agents-py/tests/test_streaming_parser.py +178 -0
  36. simple_agents_py-0.1.14/crates/simple-agents-py/tests/test_structured_streaming.py +184 -0
  37. simple_agents_py-0.1.14/crates/simple-agents-py/uv.lock +199 -0
  38. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-router/Cargo.toml +1 -0
  39. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-router/src/cost.rs +13 -2
  40. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-router/src/fallback.rs +22 -1
  41. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-router/src/latency.rs +13 -2
  42. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-router/src/round_robin.rs +17 -1
  43. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/pyproject.toml +1 -1
  44. simple_agents_py-0.1.11/crates/simple-agents-py/src/lib.rs +0 -411
  45. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agent-type/Cargo.toml +0 -0
  46. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agent-type/README.md +0 -0
  47. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agent-type/TEST_GUIDE.md +0 -0
  48. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agent-type/TODO.md +0 -0
  49. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agent-type/examples/basic_usage.rs +0 -0
  50. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agent-type/examples/mock_provider.rs +0 -0
  51. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agent-type/src/cache.rs +0 -0
  52. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agent-type/src/coercion.rs +0 -0
  53. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agent-type/src/config.rs +0 -0
  54. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agent-type/src/error.rs +0 -0
  55. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agent-type/src/response.rs +0 -0
  56. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agent-type/src/router.rs +0 -0
  57. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agent-type/src/validation.rs +0 -0
  58. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agent-type/tests/integration_test.rs +0 -0
  59. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-cache/Cargo.toml +0 -0
  60. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-cache/src/lib.rs +0 -0
  61. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-cache/src/memory.rs +0 -0
  62. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-cache/src/noop.rs +0 -0
  63. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-core/README.md +0 -0
  64. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-core/examples/basic_client.rs +0 -0
  65. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-core/src/healing.rs +0 -0
  66. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-core/src/lib.rs +0 -0
  67. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-core/src/middleware.rs +0 -0
  68. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-core/tests/client_integration.rs +0 -0
  69. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-healing/README.md +0 -0
  70. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-healing/benches/parser_benchmarks.rs +0 -0
  71. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-healing/examples/basic_healing.rs +0 -0
  72. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-healing/examples/coercion_demo.rs +0 -0
  73. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-healing/examples/streaming_annotations.rs +0 -0
  74. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-healing/examples/streaming_partial_types.rs +0 -0
  75. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-healing/src/coercion.rs +0 -0
  76. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-healing/src/lib.rs +0 -0
  77. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-healing/src/parser.rs +0 -0
  78. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-healing/src/schema.rs +0 -0
  79. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-healing/src/streaming.rs +0 -0
  80. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-healing/src/string_utils.rs +0 -0
  81. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-healing/tests/parser_tests.rs +0 -0
  82. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-healing/tests/property_tests.rs +0 -0
  83. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-healing/tests/stream_annotations_tests.rs +0 -0
  84. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-healing/tests/streaming_tests.rs +0 -0
  85. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-macros/Cargo.toml +0 -0
  86. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-macros/README.md +0 -0
  87. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-macros/src/lib.rs +0 -0
  88. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-macros/src/partial.rs +0 -0
  89. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-macros/tests/partial_type_tests.rs +0 -0
  90. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/README.md +0 -0
  91. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/examples/anthropic_basic.rs +0 -0
  92. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/examples/anthropic_structured_output.rs +0 -0
  93. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/examples/cache_usage.rs +0 -0
  94. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/examples/custom_api.rs +0 -0
  95. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/examples/healing_fallback.rs +0 -0
  96. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/examples/openai_basic.rs +0 -0
  97. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/examples/openai_structured_output.rs +0 -0
  98. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/examples/openrouter_basic.rs +0 -0
  99. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/examples/retry_demo.rs +0 -0
  100. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/examples/streaming.rs +0 -0
  101. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/examples/streaming_structured.rs +0 -0
  102. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/examples/streaming_with_healing.rs +0 -0
  103. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/examples/test_local_api.rs +0 -0
  104. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/examples/test_reqwest.rs +0 -0
  105. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/run_integration_tests.sh +0 -0
  106. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/src/anthropic/error.rs +0 -0
  107. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/src/anthropic/models.rs +0 -0
  108. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/src/anthropic/streaming.rs +0 -0
  109. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/src/common/error.rs +0 -0
  110. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/src/common/http_client.rs +0 -0
  111. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/src/common/mod.rs +0 -0
  112. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/src/healing_integration.rs +0 -0
  113. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/src/lib.rs +0 -0
  114. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/src/metrics.rs +0 -0
  115. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/src/openai/error.rs +0 -0
  116. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/src/openai/streaming.rs +0 -0
  117. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/src/rate_limit.rs +0 -0
  118. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/src/retry.rs +0 -0
  119. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/src/schema_converter.rs +0 -0
  120. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/src/streaming_structured.rs +0 -0
  121. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/src/utils.rs +0 -0
  122. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/tests/README.md +0 -0
  123. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/tests/healing_integration_tests.rs +0 -0
  124. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-providers/tests/openai_integration.rs +0 -0
  125. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-py/py.typed +0 -0
  126. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-py/simple_agents_py.pyi +0 -0
  127. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-py/tests/test_client.py +0 -0
  128. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-router/examples/round_robin_router.rs +0 -0
  129. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-router/src/circuit_breaker.rs +0 -0
  130. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-router/src/health.rs +0 -0
  131. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-router/src/lib.rs +0 -0
  132. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-router/src/retry.rs +0 -0
  133. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/crates/simple-agents-router/tests/health_tracker_integration.rs +0 -0
  134. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/py.typed +0 -0
  135. {simple_agents_py-0.1.11 → simple_agents_py-0.1.14}/simple_agents_py.pyi +0 -0
@@ -2035,7 +2035,7 @@ dependencies = [
2035
2035
 
2036
2036
  [[package]]
2037
2037
  name = "simple-agent-type"
2038
- version = "0.1.11"
2038
+ version = "0.1.14"
2039
2039
  dependencies = [
2040
2040
  "async-trait",
2041
2041
  "blake3",
@@ -2050,7 +2050,7 @@ dependencies = [
2050
2050
 
2051
2051
  [[package]]
2052
2052
  name = "simple-agents-cache"
2053
- version = "0.1.11"
2053
+ version = "0.1.14"
2054
2054
  dependencies = [
2055
2055
  "async-trait",
2056
2056
  "simple-agent-type",
@@ -2059,7 +2059,7 @@ dependencies = [
2059
2059
 
2060
2060
  [[package]]
2061
2061
  name = "simple-agents-cli"
2062
- version = "0.1.11"
2062
+ version = "0.1.14"
2063
2063
  dependencies = [
2064
2064
  "clap",
2065
2065
  "serde",
@@ -2076,9 +2076,10 @@ dependencies = [
2076
2076
 
2077
2077
  [[package]]
2078
2078
  name = "simple-agents-core"
2079
- version = "0.1.11"
2079
+ version = "0.1.14"
2080
2080
  dependencies = [
2081
2081
  "async-trait",
2082
+ "futures-core",
2082
2083
  "serde",
2083
2084
  "serde_json",
2084
2085
  "simple-agent-type",
@@ -2103,7 +2104,7 @@ dependencies = [
2103
2104
 
2104
2105
  [[package]]
2105
2106
  name = "simple-agents-ffi"
2106
- version = "0.1.11"
2107
+ version = "0.1.14"
2107
2108
  dependencies = [
2108
2109
  "async-trait",
2109
2110
  "simple-agent-type",
@@ -2114,7 +2115,7 @@ dependencies = [
2114
2115
 
2115
2116
  [[package]]
2116
2117
  name = "simple-agents-healing"
2117
- version = "0.1.11"
2118
+ version = "0.1.14"
2118
2119
  dependencies = [
2119
2120
  "criterion",
2120
2121
  "proptest",
@@ -2130,7 +2131,7 @@ dependencies = [
2130
2131
 
2131
2132
  [[package]]
2132
2133
  name = "simple-agents-macros"
2133
- version = "0.1.11"
2134
+ version = "0.1.14"
2134
2135
  dependencies = [
2135
2136
  "proc-macro2",
2136
2137
  "quote",
@@ -2142,7 +2143,7 @@ dependencies = [
2142
2143
 
2143
2144
  [[package]]
2144
2145
  name = "simple-agents-napi"
2145
- version = "0.1.11"
2146
+ version = "0.1.14"
2146
2147
  dependencies = [
2147
2148
  "napi",
2148
2149
  "napi-derive",
@@ -2154,7 +2155,7 @@ dependencies = [
2154
2155
 
2155
2156
  [[package]]
2156
2157
  name = "simple-agents-providers"
2157
- version = "0.1.11"
2158
+ version = "0.1.14"
2158
2159
  dependencies = [
2159
2160
  "async-trait",
2160
2161
  "bytes",
@@ -2182,23 +2183,29 @@ dependencies = [
2182
2183
 
2183
2184
  [[package]]
2184
2185
  name = "simple-agents-py"
2185
- version = "0.1.11"
2186
+ version = "0.1.14"
2186
2187
  dependencies = [
2188
+ "async-trait",
2189
+ "futures-util",
2187
2190
  "pyo3",
2188
2191
  "pythonize",
2189
2192
  "reqwest",
2190
2193
  "serde_json",
2191
2194
  "simple-agent-type",
2195
+ "simple-agents-cache",
2192
2196
  "simple-agents-core",
2197
+ "simple-agents-healing",
2193
2198
  "simple-agents-providers",
2199
+ "simple-agents-router",
2194
2200
  "tokio",
2195
2201
  ]
2196
2202
 
2197
2203
  [[package]]
2198
2204
  name = "simple-agents-router"
2199
- version = "0.1.11"
2205
+ version = "0.1.14"
2200
2206
  dependencies = [
2201
2207
  "async-trait",
2208
+ "futures-core",
2202
2209
  "rand 0.8.5",
2203
2210
  "serde_json",
2204
2211
  "simple-agent-type",
@@ -3,7 +3,7 @@ members = ["crates/*"]
3
3
  resolver = "2"
4
4
 
5
5
  [workspace.package]
6
- version = "0.1.11"
6
+ version = "0.1.14"
7
7
  edition = "2021"
8
8
  rust-version = "1.75"
9
9
  authors = ["SimpleAgents Contributors"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simple-agents-py
3
- Version: 0.1.11
3
+ Version: 0.1.14
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Rust
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -60,6 +60,121 @@ response = client.complete("gpt-4", "Hello from Python!", max_tokens=128, temper
60
60
  print(response)
61
61
  ```
62
62
 
63
+ ## Feature Guide
64
+
65
+ ### Streaming
66
+
67
+ ```python
68
+ from simple_agents_py import Client
69
+
70
+ client = Client("openai")
71
+ messages = [{"role": "user", "content": "Say hello in one sentence."}]
72
+ for chunk in client.stream("gpt-4o-mini", messages, max_tokens=64):
73
+ if chunk.content:
74
+ print(chunk.content, end="", flush=True)
75
+ print()
76
+ ```
77
+
78
+ ### Structured Output (JSON Mode)
79
+
80
+ ```python
81
+ from simple_agents_py import Client
82
+ import json
83
+
84
+ client = Client("openai")
85
+ schema = {
86
+ "type": "object",
87
+ "properties": {
88
+ "name": {"type": "string"},
89
+ "age": {"type": "number"},
90
+ },
91
+ "required": ["name", "age"],
92
+ }
93
+ messages = [{"role": "user", "content": "Extract name and age: Alice is 28."}]
94
+ json_text = client.complete_json_schema("gpt-4o-mini", messages, schema, "person")
95
+ print(json.loads(json_text))
96
+ ```
97
+
98
+ ### Structured Streaming
99
+
100
+ ```python
101
+ from simple_agents_py import Client
102
+
103
+ client = Client("openai")
104
+ schema = {
105
+ "type": "object",
106
+ "properties": {"name": {"type": "string"}, "age": {"type": "number"}},
107
+ "required": ["name", "age"],
108
+ }
109
+ messages = [{"role": "user", "content": "Extract name and age: Alice is 28."}]
110
+ for event in client.stream_structured("gpt-4o-mini", messages, schema, max_tokens=64):
111
+ if event.is_partial:
112
+ print("partial:", event.partial_value)
113
+ else:
114
+ print("complete:", event.value)
115
+ ```
116
+
117
+ ### Response Healing
118
+
119
+ ```python
120
+ from simple_agents_py import Client
121
+
122
+ client = Client("openai")
123
+ messages = [{"role": "user", "content": "Return JSON: {\"name\":\"Sam\",\"age\":30}"}]
124
+ healed = client.complete_json_healed("gpt-4o-mini", messages, max_tokens=64)
125
+ print(healed.content)
126
+ print(healed.was_healed, healed.confidence)
127
+ ```
128
+
129
+ ### Tool Calling
130
+
131
+ ```python
132
+ from simple_agents_py import Client
133
+
134
+ client = Client("openai")
135
+ tools = [
136
+ {
137
+ "type": "function",
138
+ "function": {
139
+ "name": "get_weather",
140
+ "description": "Get the weather for a city",
141
+ "parameters": {
142
+ "type": "object",
143
+ "properties": {
144
+ "city": {"type": "string"},
145
+ "unit": {"type": "string", "enum": ["c", "f"]},
146
+ },
147
+ "required": ["city"],
148
+ },
149
+ },
150
+ }
151
+ ]
152
+ messages = [{"role": "user", "content": "What's the weather in Tokyo?"}]
153
+ response = client.complete_with_tools("gpt-4o-mini", messages, tools)
154
+ print(response.tool_calls)
155
+ ```
156
+
157
+ ### ClientBuilder (Routing, Cache, Healing, Middleware)
158
+
159
+ ```python
160
+ from simple_agents_py import ClientBuilder
161
+
162
+ class TimingMiddleware:
163
+ def before_request(self, request):
164
+ print("sending", request.model)
165
+
166
+ builder = (
167
+ ClientBuilder()
168
+ .add_provider("openai", api_key="sk-...")
169
+ .with_routing("direct")
170
+ .with_cache(ttl_seconds=60)
171
+ .with_healing_config({"enabled": True, "min_confidence": 0.7})
172
+ .add_middleware(TimingMiddleware())
173
+ )
174
+ client = builder.build()
175
+ print(client.complete("gpt-4o-mini", "Give me one idea."))
176
+ ```
177
+
63
178
  ## Examples
64
179
 
65
180
  OpenAI with a short prompt:
@@ -45,6 +45,121 @@ response = client.complete("gpt-4", "Hello from Python!", max_tokens=128, temper
45
45
  print(response)
46
46
  ```
47
47
 
48
+ ## Feature Guide
49
+
50
+ ### Streaming
51
+
52
+ ```python
53
+ from simple_agents_py import Client
54
+
55
+ client = Client("openai")
56
+ messages = [{"role": "user", "content": "Say hello in one sentence."}]
57
+ for chunk in client.stream("gpt-4o-mini", messages, max_tokens=64):
58
+ if chunk.content:
59
+ print(chunk.content, end="", flush=True)
60
+ print()
61
+ ```
62
+
63
+ ### Structured Output (JSON Mode)
64
+
65
+ ```python
66
+ from simple_agents_py import Client
67
+ import json
68
+
69
+ client = Client("openai")
70
+ schema = {
71
+ "type": "object",
72
+ "properties": {
73
+ "name": {"type": "string"},
74
+ "age": {"type": "number"},
75
+ },
76
+ "required": ["name", "age"],
77
+ }
78
+ messages = [{"role": "user", "content": "Extract name and age: Alice is 28."}]
79
+ json_text = client.complete_json_schema("gpt-4o-mini", messages, schema, "person")
80
+ print(json.loads(json_text))
81
+ ```
82
+
83
+ ### Structured Streaming
84
+
85
+ ```python
86
+ from simple_agents_py import Client
87
+
88
+ client = Client("openai")
89
+ schema = {
90
+ "type": "object",
91
+ "properties": {"name": {"type": "string"}, "age": {"type": "number"}},
92
+ "required": ["name", "age"],
93
+ }
94
+ messages = [{"role": "user", "content": "Extract name and age: Alice is 28."}]
95
+ for event in client.stream_structured("gpt-4o-mini", messages, schema, max_tokens=64):
96
+ if event.is_partial:
97
+ print("partial:", event.partial_value)
98
+ else:
99
+ print("complete:", event.value)
100
+ ```
101
+
102
+ ### Response Healing
103
+
104
+ ```python
105
+ from simple_agents_py import Client
106
+
107
+ client = Client("openai")
108
+ messages = [{"role": "user", "content": "Return JSON: {\"name\":\"Sam\",\"age\":30}"}]
109
+ healed = client.complete_json_healed("gpt-4o-mini", messages, max_tokens=64)
110
+ print(healed.content)
111
+ print(healed.was_healed, healed.confidence)
112
+ ```
113
+
114
+ ### Tool Calling
115
+
116
+ ```python
117
+ from simple_agents_py import Client
118
+
119
+ client = Client("openai")
120
+ tools = [
121
+ {
122
+ "type": "function",
123
+ "function": {
124
+ "name": "get_weather",
125
+ "description": "Get the weather for a city",
126
+ "parameters": {
127
+ "type": "object",
128
+ "properties": {
129
+ "city": {"type": "string"},
130
+ "unit": {"type": "string", "enum": ["c", "f"]},
131
+ },
132
+ "required": ["city"],
133
+ },
134
+ },
135
+ }
136
+ ]
137
+ messages = [{"role": "user", "content": "What's the weather in Tokyo?"}]
138
+ response = client.complete_with_tools("gpt-4o-mini", messages, tools)
139
+ print(response.tool_calls)
140
+ ```
141
+
142
+ ### ClientBuilder (Routing, Cache, Healing, Middleware)
143
+
144
+ ```python
145
+ from simple_agents_py import ClientBuilder
146
+
147
+ class TimingMiddleware:
148
+ def before_request(self, request):
149
+ print("sending", request.model)
150
+
151
+ builder = (
152
+ ClientBuilder()
153
+ .add_provider("openai", api_key="sk-...")
154
+ .with_routing("direct")
155
+ .with_cache(ttl_seconds=60)
156
+ .with_healing_config({"enabled": True, "min_confidence": 0.7})
157
+ .add_middleware(TimingMiddleware())
158
+ )
159
+ client = builder.build()
160
+ print(client.complete("gpt-4o-mini", "Give me one idea."))
161
+ ```
162
+
48
163
  ## Examples
49
164
 
50
165
  OpenAI with a short prompt:
@@ -58,6 +58,7 @@ pub mod provider;
58
58
  pub mod request;
59
59
  pub mod response;
60
60
  pub mod router;
61
+ pub mod tool;
61
62
  pub mod validation;
62
63
 
63
64
  // Re-export commonly used types at crate root
@@ -101,6 +102,12 @@ pub mod prelude {
101
102
  // Coercion
102
103
  pub use crate::coercion::{CoercionFlag, CoercionResult};
103
104
 
105
+ // Tool calling
106
+ pub use crate::tool::{
107
+ ToolCall, ToolCallFunction, ToolChoice, ToolChoiceFunction, ToolChoiceMode, ToolChoiceTool,
108
+ ToolDefinition, ToolFunction, ToolType,
109
+ };
110
+
104
111
  // Traits
105
112
  pub use crate::cache::Cache;
106
113
  pub use crate::provider::Provider;
@@ -4,6 +4,8 @@
4
4
 
5
5
  use serde::{Deserialize, Serialize};
6
6
 
7
+ use crate::tool::ToolCall;
8
+
7
9
  /// Role of a message in a conversation.
8
10
  #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9
11
  #[serde(rename_all = "lowercase")]
@@ -32,6 +34,9 @@ pub struct Message {
32
34
  /// Tool call ID (for tool role messages)
33
35
  #[serde(skip_serializing_if = "Option::is_none")]
34
36
  pub tool_call_id: Option<String>,
37
+ /// Tool calls emitted by the assistant.
38
+ #[serde(skip_serializing_if = "Option::is_none")]
39
+ pub tool_calls: Option<Vec<ToolCall>>,
35
40
  }
36
41
 
37
42
  impl Message {
@@ -51,6 +56,7 @@ impl Message {
51
56
  content: content.into(),
52
57
  name: None,
53
58
  tool_call_id: None,
59
+ tool_calls: None,
54
60
  }
55
61
  }
56
62
 
@@ -69,6 +75,7 @@ impl Message {
69
75
  content: content.into(),
70
76
  name: None,
71
77
  tool_call_id: None,
78
+ tool_calls: None,
72
79
  }
73
80
  }
74
81
 
@@ -87,6 +94,7 @@ impl Message {
87
94
  content: content.into(),
88
95
  name: None,
89
96
  tool_call_id: None,
97
+ tool_calls: None,
90
98
  }
91
99
  }
92
100
 
@@ -106,6 +114,7 @@ impl Message {
106
114
  content: content.into(),
107
115
  name: None,
108
116
  tool_call_id: Some(tool_call_id.into()),
117
+ tool_calls: None,
109
118
  }
110
119
  }
111
120
 
@@ -122,6 +131,12 @@ impl Message {
122
131
  self.name = Some(name.into());
123
132
  self
124
133
  }
134
+
135
+ /// Set tool calls for assistant messages.
136
+ pub fn with_tool_calls(mut self, tool_calls: Vec<ToolCall>) -> Self {
137
+ self.tool_calls = Some(tool_calls);
138
+ self
139
+ }
125
140
  }
126
141
 
127
142
  #[cfg(test)]
@@ -135,6 +150,7 @@ mod tests {
135
150
  assert_eq!(msg.content, "test");
136
151
  assert_eq!(msg.name, None);
137
152
  assert_eq!(msg.tool_call_id, None);
153
+ assert_eq!(msg.tool_calls, None);
138
154
  }
139
155
 
140
156
  #[test]
@@ -142,6 +158,7 @@ mod tests {
142
158
  let msg = Message::assistant("response");
143
159
  assert_eq!(msg.role, Role::Assistant);
144
160
  assert_eq!(msg.content, "response");
161
+ assert_eq!(msg.tool_calls, None);
145
162
  }
146
163
 
147
164
  #[test]
@@ -149,6 +166,7 @@ mod tests {
149
166
  let msg = Message::system("instruction");
150
167
  assert_eq!(msg.role, Role::System);
151
168
  assert_eq!(msg.content, "instruction");
169
+ assert_eq!(msg.tool_calls, None);
152
170
  }
153
171
 
154
172
  #[test]
@@ -157,6 +175,7 @@ mod tests {
157
175
  assert_eq!(msg.role, Role::Tool);
158
176
  assert_eq!(msg.content, "result");
159
177
  assert_eq!(msg.tool_call_id, Some("call_123".to_string()));
178
+ assert_eq!(msg.tool_calls, None);
160
179
  }
161
180
 
162
181
  #[test]
@@ -194,6 +213,7 @@ mod tests {
194
213
  let json = serde_json::to_value(&msg).unwrap();
195
214
  assert!(json.get("name").is_none());
196
215
  assert!(json.get("tool_call_id").is_none());
216
+ assert!(json.get("tool_calls").is_none());
197
217
  }
198
218
 
199
219
  #[test]
@@ -3,11 +3,12 @@
3
3
  //! Defines the interface for LLM providers with transformation hooks.
4
4
 
5
5
  use crate::config::{Capabilities, RetryConfig};
6
- use crate::error::{ProviderError, Result, SimpleAgentsError};
6
+ use crate::error::Result;
7
7
  use crate::request::CompletionRequest;
8
8
  use crate::response::{CompletionChunk, CompletionResponse};
9
9
  use async_trait::async_trait;
10
10
  use serde::{Deserialize, Serialize};
11
+ use serde_json::Value;
11
12
  use std::borrow::Cow;
12
13
  use std::time::Duration;
13
14
 
@@ -236,11 +237,55 @@ pub trait Provider: Send + Sync {
236
237
  /// ```
237
238
  async fn execute_stream(
238
239
  &self,
239
- _req: ProviderRequest,
240
+ mut req: ProviderRequest,
240
241
  ) -> Result<Box<dyn futures_core::Stream<Item = Result<CompletionChunk>> + Send + Unpin>> {
241
- Err(SimpleAgentsError::Provider(
242
- ProviderError::UnsupportedFeature("streaming".to_string()),
243
- ))
242
+ if let Value::Object(map) = &mut req.body {
243
+ if let Some(stream_value) = map.get_mut("stream") {
244
+ *stream_value = Value::Bool(false);
245
+ }
246
+ }
247
+
248
+ let provider_response = self.execute(req).await?;
249
+ let response = self.transform_response(provider_response)?;
250
+
251
+ struct SingleChunkStream {
252
+ chunk: Option<Result<CompletionChunk>>,
253
+ }
254
+
255
+ impl futures_core::Stream for SingleChunkStream {
256
+ type Item = Result<CompletionChunk>;
257
+
258
+ fn poll_next(
259
+ mut self: std::pin::Pin<&mut Self>,
260
+ _cx: &mut std::task::Context<'_>,
261
+ ) -> std::task::Poll<Option<Self::Item>> {
262
+ std::task::Poll::Ready(self.chunk.take())
263
+ }
264
+ }
265
+
266
+ let choices = response
267
+ .choices
268
+ .into_iter()
269
+ .map(|choice| crate::response::ChoiceDelta {
270
+ index: choice.index,
271
+ delta: crate::response::MessageDelta {
272
+ role: Some(choice.message.role),
273
+ content: Some(choice.message.content),
274
+ },
275
+ finish_reason: Some(choice.finish_reason),
276
+ })
277
+ .collect();
278
+
279
+ let chunk = CompletionChunk {
280
+ id: response.id,
281
+ model: response.model,
282
+ choices,
283
+ created: response.created,
284
+ };
285
+
286
+ Ok(Box::new(SingleChunkStream {
287
+ chunk: Some(Ok(chunk)),
288
+ }))
244
289
  }
245
290
  }
246
291
 
@@ -4,6 +4,7 @@
4
4
 
5
5
  use crate::error::{Result, ValidationError};
6
6
  use crate::message::Message;
7
+ use crate::tool::{ToolChoice, ToolDefinition};
7
8
  use serde::{Deserialize, Serialize};
8
9
  use serde_json::Value;
9
10
 
@@ -72,6 +73,12 @@ pub struct CompletionRequest {
72
73
  /// Response format for structured outputs (OpenAI only)
73
74
  #[serde(skip_serializing_if = "Option::is_none")]
74
75
  pub response_format: Option<ResponseFormat>,
76
+ /// Tool definitions for tool calling.
77
+ #[serde(skip_serializing_if = "Option::is_none")]
78
+ pub tools: Option<Vec<ToolDefinition>>,
79
+ /// Tool choice configuration.
80
+ #[serde(skip_serializing_if = "Option::is_none")]
81
+ pub tool_choice: Option<ToolChoice>,
75
82
  }
76
83
 
77
84
  impl CompletionRequest {
@@ -237,6 +244,8 @@ pub struct CompletionRequestBuilder {
237
244
  frequency_penalty: Option<f32>,
238
245
  user: Option<String>,
239
246
  response_format: Option<ResponseFormat>,
247
+ tools: Option<Vec<ToolDefinition>>,
248
+ tool_choice: Option<ToolChoice>,
240
249
  }
241
250
 
242
251
  impl CompletionRequestBuilder {
@@ -318,6 +327,18 @@ impl CompletionRequestBuilder {
318
327
  self
319
328
  }
320
329
 
330
+ /// Set tool definitions for tool calling.
331
+ pub fn tools(mut self, tools: Vec<ToolDefinition>) -> Self {
332
+ self.tools = Some(tools);
333
+ self
334
+ }
335
+
336
+ /// Set tool choice configuration.
337
+ pub fn tool_choice(mut self, tool_choice: ToolChoice) -> Self {
338
+ self.tool_choice = Some(tool_choice);
339
+ self
340
+ }
341
+
321
342
  /// Enable JSON object mode (no schema validation).
322
343
  pub fn json_mode(mut self) -> Self {
323
344
  self.response_format = Some(ResponseFormat::JsonObject);
@@ -355,6 +376,8 @@ impl CompletionRequestBuilder {
355
376
  frequency_penalty: self.frequency_penalty,
356
377
  user: self.user,
357
378
  response_format: self.response_format,
379
+ tools: self.tools,
380
+ tool_choice: self.tool_choice,
358
381
  };
359
382
 
360
383
  request.validate()?;