cortex-agent-sdk 0.2.0__tar.gz → 0.3.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 (19) hide show
  1. {cortex_agent_sdk-0.2.0 → cortex_agent_sdk-0.3.0}/PKG-INFO +35 -20
  2. {cortex_agent_sdk-0.2.0 → cortex_agent_sdk-0.3.0}/README.md +32 -13
  3. {cortex_agent_sdk-0.2.0 → cortex_agent_sdk-0.3.0}/pyproject.toml +4 -11
  4. cortex_agent_sdk-0.3.0/src/cortex_agent_sdk/capabilities.py +62 -0
  5. {cortex_agent_sdk-0.2.0 → cortex_agent_sdk-0.3.0}/.gitignore +0 -0
  6. {cortex_agent_sdk-0.2.0 → cortex_agent_sdk-0.3.0}/LICENSE +0 -0
  7. {cortex_agent_sdk-0.2.0 → cortex_agent_sdk-0.3.0}/src/cortex_agent_sdk/__init__.py +0 -0
  8. {cortex_agent_sdk-0.2.0 → cortex_agent_sdk-0.3.0}/src/cortex_agent_sdk/errores/__init__.py +0 -0
  9. {cortex_agent_sdk-0.2.0 → cortex_agent_sdk-0.3.0}/src/cortex_agent_sdk/errores/catalogo.py +0 -0
  10. {cortex_agent_sdk-0.2.0 → cortex_agent_sdk-0.3.0}/src/cortex_agent_sdk/errores/excepcion.py +0 -0
  11. {cortex_agent_sdk-0.2.0 → cortex_agent_sdk-0.3.0}/src/cortex_agent_sdk/py.typed +0 -0
  12. {cortex_agent_sdk-0.2.0 → cortex_agent_sdk-0.3.0}/src/cortex_agent_sdk/redis/__init__.py +0 -0
  13. {cortex_agent_sdk-0.2.0 → cortex_agent_sdk-0.3.0}/src/cortex_agent_sdk/redis/scripts.py +0 -0
  14. {cortex_agent_sdk-0.2.0 → cortex_agent_sdk-0.3.0}/src/cortex_agent_sdk/redis/store.py +0 -0
  15. {cortex_agent_sdk-0.2.0 → cortex_agent_sdk-0.3.0}/src/cortex_agent_sdk/sessions/__init__.py +0 -0
  16. {cortex_agent_sdk-0.2.0 → cortex_agent_sdk-0.3.0}/src/cortex_agent_sdk/sessions/codec.py +0 -0
  17. {cortex_agent_sdk-0.2.0 → cortex_agent_sdk-0.3.0}/src/cortex_agent_sdk/sessions/memory.py +0 -0
  18. {cortex_agent_sdk-0.2.0 → cortex_agent_sdk-0.3.0}/src/cortex_agent_sdk/sessions/models.py +0 -0
  19. {cortex_agent_sdk-0.2.0 → cortex_agent_sdk-0.3.0}/src/cortex_agent_sdk/sessions/store.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cortex-agent-sdk
3
- Version: 0.2.0
4
- Summary: Sesiones Memory y Redis para agentes construidos con Pydantic AI
3
+ Version: 0.3.0
4
+ Summary: Sesiones y capacidades puntuales para agentes multiprovider con Pydantic AI
5
5
  Project-URL: Repository, https://github.com/epok200/cortex_agent_sdk
6
6
  Project-URL: Issues, https://github.com/epok200/cortex_agent_sdk/issues
7
7
  Author: EPOK
@@ -16,51 +16,46 @@ Classifier: Programming Language :: Python :: 3
16
16
  Classifier: Programming Language :: Python :: 3.13
17
17
  Classifier: Typing :: Typed
18
18
  Requires-Python: >=3.13
19
- Requires-Dist: pydantic-ai-slim<3,>=2.27
20
- Provides-Extra: all
21
- Requires-Dist: pydantic-ai-slim[openai]<3,>=2.27; extra == 'all'
22
- Requires-Dist: redis>=8.1.0; extra == 'all'
19
+ Requires-Dist: pydantic-ai-slim[google,openai]<3,>=2.27
23
20
  Provides-Extra: openai
24
- Requires-Dist: pydantic-ai-slim[openai]<3,>=2.27; extra == 'openai'
25
21
  Provides-Extra: redis
26
22
  Requires-Dist: redis>=8.1.0; extra == 'redis'
27
23
  Description-Content-Type: text/markdown
28
24
 
29
25
  # Cortex Agent SDK
30
26
 
31
- Cortex agrega sesiones Memory y Redis a agentes construidos directamente con Pydantic AI.
27
+ Cortex agrega sesiones Memory y Redis, junto con capacidades puntuales que Pydantic AI no incluye,
28
+ a agentes multiprovider construidos directamente con Pydantic AI.
32
29
 
33
30
  No implementa otro loop, otra capa de tools ni otra API de agentes. Pydantic AI conserva el control
34
31
  de providers, modelos, tools, tipado, `RunContext`, hooks, límites, approvals, outputs, usage e
35
- historial. Cortex sólo cubre la pieza que Pydantic AI no incluye: persistencia conversacional con un
36
- turno activo por sesión.
32
+ historial. Cortex aporta persistencia conversacional con un turno activo por sesión y capacidades
33
+ opcionales construidas sobre sus hooks públicos.
37
34
 
38
35
  > Cortex Agent SDK está en alfa. La API puede cambiar antes de la versión `1.0.0`.
39
36
 
40
37
  ## Requisitos
41
38
 
42
39
  - Python `>=3.13`.
43
- - Pydantic AI `>=2.27,<3`, instalado por Cortex en su variante slim.
40
+ - Pydantic AI `>=2.27,<3`, con Google y OpenAI instalados por Cortex.
44
41
 
45
42
  ## Instalación
46
43
 
47
- Sesiones en memoria:
44
+ Google, OpenAI y sesiones en memoria:
48
45
 
49
46
  ```bash
50
47
  uv add cortex-agent-sdk
51
48
  ```
52
49
 
53
- OpenAI Responses:
50
+ Google, OpenAI y Redis:
54
51
 
55
52
  ```bash
56
- uv add "cortex-agent-sdk[openai]"
53
+ uv add "cortex-agent-sdk[redis]"
57
54
  ```
58
55
 
59
- OpenAI Responses y Redis:
60
-
61
- ```bash
62
- uv add "cortex-agent-sdk[openai,redis]"
63
- ```
56
+ La instalación base incluye Google y OpenAI, además de los endpoints compatibles con OpenAI. Otros
57
+ providers pueden agregarse desde los extras oficiales de Pydantic AI cuando un producto realmente
58
+ los necesite. Cortex no implementa adapters paralelos.
64
59
 
65
60
  ## Uso
66
61
 
@@ -141,6 +136,25 @@ agent = Agent(model)
141
136
 
142
137
  El context manager de `Agent` administra el transporte del provider.
143
138
 
139
+ ## Fallback del resultado de una tool
140
+
141
+ Pydantic AI reintenta cuando un modelo termina sin texto. Para tools cuyo resultado ya es una
142
+ respuesta completa, Cortex puede reutilizar el último resultado exitoso del mismo run:
143
+
144
+ ```python
145
+ from pydantic_ai import Agent
146
+
147
+ from cortex_agent_sdk.capabilities import last_tool_result_fallback
148
+
149
+ agent = Agent(
150
+ "openai-responses:gpt-5.6-luna",
151
+ capabilities=[last_tool_result_fallback({"confirmar_agenda"})],
152
+ )
153
+ ```
154
+
155
+ La aplicación conserva la decisión sobre las tools elegibles. La capacidad no usa resultados
156
+ fallidos, vacíos ni pertenecientes a otro run, y no reemplaza texto o nuevas llamadas del modelo.
157
+
144
158
  ## Migración desde el runtime anterior
145
159
 
146
160
  | Antes | Ahora |
@@ -154,7 +168,7 @@ El context manager de `Agent` administra el transporte del provider.
154
168
  | `AgentHooks` | `pydantic_ai.capabilities.Hooks` |
155
169
  | `turn_finished` | `Hooks(after_run=...)` |
156
170
  | `history_transform` | `Hooks(before_model_request=...)` |
157
- | `fallback_answer` | política local del producto sobre `AgentRunResult` |
171
+ | `fallback_answer` | `last_tool_result_fallback(...)` opcional |
158
172
  | `AgentOptions` | `UsageLimits`, settings del modelo y argumentos de `Agent` |
159
173
  | `AgentResult.text` | `AgentRunResult.output` |
160
174
  | `SessionStore.acquire` | `SessionStore.turn` + `Session.replace` |
@@ -169,6 +183,7 @@ Pydantic AI.
169
183
  - `cortex_agent_sdk.sessions.SessionStore`
170
184
  - `cortex_agent_sdk.sessions.MemorySessionStore`
171
185
  - `cortex_agent_sdk.redis.RedisSessionStore`
186
+ - `cortex_agent_sdk.capabilities.last_tool_result_fallback`
172
187
  - `cortex_agent_sdk.errores.AppError`
173
188
  - `cortex_agent_sdk.errores.CodigoError`
174
189
  - `cortex_agent_sdk.errores.Severidad`
@@ -1,38 +1,37 @@
1
1
  # Cortex Agent SDK
2
2
 
3
- Cortex agrega sesiones Memory y Redis a agentes construidos directamente con Pydantic AI.
3
+ Cortex agrega sesiones Memory y Redis, junto con capacidades puntuales que Pydantic AI no incluye,
4
+ a agentes multiprovider construidos directamente con Pydantic AI.
4
5
 
5
6
  No implementa otro loop, otra capa de tools ni otra API de agentes. Pydantic AI conserva el control
6
7
  de providers, modelos, tools, tipado, `RunContext`, hooks, límites, approvals, outputs, usage e
7
- historial. Cortex sólo cubre la pieza que Pydantic AI no incluye: persistencia conversacional con un
8
- turno activo por sesión.
8
+ historial. Cortex aporta persistencia conversacional con un turno activo por sesión y capacidades
9
+ opcionales construidas sobre sus hooks públicos.
9
10
 
10
11
  > Cortex Agent SDK está en alfa. La API puede cambiar antes de la versión `1.0.0`.
11
12
 
12
13
  ## Requisitos
13
14
 
14
15
  - Python `>=3.13`.
15
- - Pydantic AI `>=2.27,<3`, instalado por Cortex en su variante slim.
16
+ - Pydantic AI `>=2.27,<3`, con Google y OpenAI instalados por Cortex.
16
17
 
17
18
  ## Instalación
18
19
 
19
- Sesiones en memoria:
20
+ Google, OpenAI y sesiones en memoria:
20
21
 
21
22
  ```bash
22
23
  uv add cortex-agent-sdk
23
24
  ```
24
25
 
25
- OpenAI Responses:
26
+ Google, OpenAI y Redis:
26
27
 
27
28
  ```bash
28
- uv add "cortex-agent-sdk[openai]"
29
+ uv add "cortex-agent-sdk[redis]"
29
30
  ```
30
31
 
31
- OpenAI Responses y Redis:
32
-
33
- ```bash
34
- uv add "cortex-agent-sdk[openai,redis]"
35
- ```
32
+ La instalación base incluye Google y OpenAI, además de los endpoints compatibles con OpenAI. Otros
33
+ providers pueden agregarse desde los extras oficiales de Pydantic AI cuando un producto realmente
34
+ los necesite. Cortex no implementa adapters paralelos.
36
35
 
37
36
  ## Uso
38
37
 
@@ -113,6 +112,25 @@ agent = Agent(model)
113
112
 
114
113
  El context manager de `Agent` administra el transporte del provider.
115
114
 
115
+ ## Fallback del resultado de una tool
116
+
117
+ Pydantic AI reintenta cuando un modelo termina sin texto. Para tools cuyo resultado ya es una
118
+ respuesta completa, Cortex puede reutilizar el último resultado exitoso del mismo run:
119
+
120
+ ```python
121
+ from pydantic_ai import Agent
122
+
123
+ from cortex_agent_sdk.capabilities import last_tool_result_fallback
124
+
125
+ agent = Agent(
126
+ "openai-responses:gpt-5.6-luna",
127
+ capabilities=[last_tool_result_fallback({"confirmar_agenda"})],
128
+ )
129
+ ```
130
+
131
+ La aplicación conserva la decisión sobre las tools elegibles. La capacidad no usa resultados
132
+ fallidos, vacíos ni pertenecientes a otro run, y no reemplaza texto o nuevas llamadas del modelo.
133
+
116
134
  ## Migración desde el runtime anterior
117
135
 
118
136
  | Antes | Ahora |
@@ -126,7 +144,7 @@ El context manager de `Agent` administra el transporte del provider.
126
144
  | `AgentHooks` | `pydantic_ai.capabilities.Hooks` |
127
145
  | `turn_finished` | `Hooks(after_run=...)` |
128
146
  | `history_transform` | `Hooks(before_model_request=...)` |
129
- | `fallback_answer` | política local del producto sobre `AgentRunResult` |
147
+ | `fallback_answer` | `last_tool_result_fallback(...)` opcional |
130
148
  | `AgentOptions` | `UsageLimits`, settings del modelo y argumentos de `Agent` |
131
149
  | `AgentResult.text` | `AgentRunResult.output` |
132
150
  | `SessionStore.acquire` | `SessionStore.turn` + `Session.replace` |
@@ -141,6 +159,7 @@ Pydantic AI.
141
159
  - `cortex_agent_sdk.sessions.SessionStore`
142
160
  - `cortex_agent_sdk.sessions.MemorySessionStore`
143
161
  - `cortex_agent_sdk.redis.RedisSessionStore`
162
+ - `cortex_agent_sdk.capabilities.last_tool_result_fallback`
144
163
  - `cortex_agent_sdk.errores.AppError`
145
164
  - `cortex_agent_sdk.errores.CodigoError`
146
165
  - `cortex_agent_sdk.errores.Severidad`
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "cortex-agent-sdk"
3
- version = "0.2.0"
4
- description = "Sesiones Memory y Redis para agentes construidos con Pydantic AI"
3
+ version = "0.3.0"
4
+ description = "Sesiones y capacidades puntuales para agentes multiprovider con Pydantic AI"
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
7
7
  requires-python = ">=3.13"
@@ -17,7 +17,7 @@ classifiers = [
17
17
  "Typing :: Typed",
18
18
  ]
19
19
  dependencies = [
20
- "pydantic-ai-slim>=2.27,<3",
20
+ "pydantic-ai-slim[google,openai]>=2.27,<3",
21
21
  ]
22
22
 
23
23
  [project.urls]
@@ -25,20 +25,13 @@ Repository = "https://github.com/epok200/cortex_agent_sdk"
25
25
  Issues = "https://github.com/epok200/cortex_agent_sdk/issues"
26
26
 
27
27
  [project.optional-dependencies]
28
- openai = [
29
- "pydantic-ai-slim[openai]>=2.27,<3",
30
- ]
31
- all = [
32
- "pydantic-ai-slim[openai]>=2.27,<3",
33
- "redis>=8.1.0",
34
- ]
28
+ openai = []
35
29
  redis = [
36
30
  "redis>=8.1.0",
37
31
  ]
38
32
 
39
33
  [dependency-groups]
40
34
  dev = [
41
- "pydantic-ai-slim[openai]>=2.27,<3",
42
35
  "pyright>=1.1.408",
43
36
  "pytest>=9.0",
44
37
  "pytest-asyncio>=1.3",
@@ -0,0 +1,62 @@
1
+ """Capacidades opcionales para agentes Pydantic AI."""
2
+
3
+ from collections.abc import Collection
4
+ from dataclasses import replace
5
+ from typing import Any
6
+
7
+ from pydantic_ai import ModelResponse, RunContext, TextPart
8
+ from pydantic_ai.capabilities.hooks import Hooks
9
+ from pydantic_ai.messages import ModelRequest, ToolReturnPart
10
+ from pydantic_ai.models import ModelRequestContext
11
+
12
+ __all__ = ["last_tool_result_fallback"]
13
+
14
+
15
+ def last_tool_result_fallback(tool_names: str | Collection[str]) -> Hooks[Any]:
16
+ """Usa el último resultado elegible si el modelo termina sin texto."""
17
+ if isinstance(tool_names, str):
18
+ eligible_tools = frozenset((tool_names,))
19
+ else:
20
+ eligible_tools = frozenset(tool_names)
21
+
22
+ def use_last_tool_result(
23
+ ctx: RunContext[Any],
24
+ *,
25
+ request_context: ModelRequestContext,
26
+ response: ModelResponse,
27
+ ) -> ModelResponse:
28
+ if response.finish_reason not in {None, "stop"}:
29
+ return response
30
+ if response.text and response.text.strip():
31
+ return response
32
+ if response.tool_calls:
33
+ return response
34
+
35
+ result = _last_tool_result(request_context, ctx.run_id, eligible_tools)
36
+ if result is None:
37
+ return response
38
+ return replace(response, parts=[*response.parts, TextPart(result)])
39
+
40
+ return Hooks(after_model_request=use_last_tool_result)
41
+
42
+
43
+ def _last_tool_result(
44
+ request_context: ModelRequestContext,
45
+ run_id: str | None,
46
+ tool_names: Collection[str],
47
+ ) -> str | None:
48
+ if run_id is None:
49
+ return None
50
+ for message in reversed(request_context.messages):
51
+ if not isinstance(message, ModelRequest) or message.run_id != run_id:
52
+ continue
53
+ for part in reversed(message.parts):
54
+ if (
55
+ isinstance(part, ToolReturnPart)
56
+ and part.outcome == "success"
57
+ and part.tool_name in tool_names
58
+ and isinstance(part.content, str)
59
+ and part.content.strip()
60
+ ):
61
+ return part.content
62
+ return None