cortex-agent-sdk 0.2.1__tar.gz → 0.3.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 (19) hide show
  1. {cortex_agent_sdk-0.2.1 → cortex_agent_sdk-0.3.1}/PKG-INFO +44 -17
  2. {cortex_agent_sdk-0.2.1 → cortex_agent_sdk-0.3.1}/README.md +38 -12
  3. {cortex_agent_sdk-0.2.1 → cortex_agent_sdk-0.3.1}/pyproject.toml +8 -6
  4. cortex_agent_sdk-0.3.1/src/cortex_agent_sdk/capabilities.py +62 -0
  5. {cortex_agent_sdk-0.2.1 → cortex_agent_sdk-0.3.1}/.gitignore +0 -0
  6. {cortex_agent_sdk-0.2.1 → cortex_agent_sdk-0.3.1}/LICENSE +0 -0
  7. {cortex_agent_sdk-0.2.1 → cortex_agent_sdk-0.3.1}/src/cortex_agent_sdk/__init__.py +0 -0
  8. {cortex_agent_sdk-0.2.1 → cortex_agent_sdk-0.3.1}/src/cortex_agent_sdk/errores/__init__.py +0 -0
  9. {cortex_agent_sdk-0.2.1 → cortex_agent_sdk-0.3.1}/src/cortex_agent_sdk/errores/catalogo.py +0 -0
  10. {cortex_agent_sdk-0.2.1 → cortex_agent_sdk-0.3.1}/src/cortex_agent_sdk/errores/excepcion.py +0 -0
  11. {cortex_agent_sdk-0.2.1 → cortex_agent_sdk-0.3.1}/src/cortex_agent_sdk/py.typed +0 -0
  12. {cortex_agent_sdk-0.2.1 → cortex_agent_sdk-0.3.1}/src/cortex_agent_sdk/redis/__init__.py +0 -0
  13. {cortex_agent_sdk-0.2.1 → cortex_agent_sdk-0.3.1}/src/cortex_agent_sdk/redis/scripts.py +0 -0
  14. {cortex_agent_sdk-0.2.1 → cortex_agent_sdk-0.3.1}/src/cortex_agent_sdk/redis/store.py +0 -0
  15. {cortex_agent_sdk-0.2.1 → cortex_agent_sdk-0.3.1}/src/cortex_agent_sdk/sessions/__init__.py +0 -0
  16. {cortex_agent_sdk-0.2.1 → cortex_agent_sdk-0.3.1}/src/cortex_agent_sdk/sessions/codec.py +0 -0
  17. {cortex_agent_sdk-0.2.1 → cortex_agent_sdk-0.3.1}/src/cortex_agent_sdk/sessions/memory.py +0 -0
  18. {cortex_agent_sdk-0.2.1 → cortex_agent_sdk-0.3.1}/src/cortex_agent_sdk/sessions/models.py +0 -0
  19. {cortex_agent_sdk-0.2.1 → cortex_agent_sdk-0.3.1}/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.1
4
- Summary: Sesiones Memory y Redis para agentes multiprovider con Pydantic AI
3
+ Version: 0.3.1
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,53 +16,60 @@ 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[google,openai]<3,>=2.27
20
- Provides-Extra: all
21
- Requires-Dist: redis>=8.1.0; extra == 'all'
19
+ Requires-Dist: pydantic-ai-slim<3,>=2.27
20
+ Provides-Extra: google
21
+ Requires-Dist: pydantic-ai-slim[google]<3,>=2.27; extra == 'google'
22
22
  Provides-Extra: openai
23
+ Requires-Dist: pydantic-ai-slim[openai]<3,>=2.27; extra == 'openai'
23
24
  Provides-Extra: redis
24
25
  Requires-Dist: redis>=8.1.0; extra == 'redis'
25
26
  Description-Content-Type: text/markdown
26
27
 
27
28
  # Cortex Agent SDK
28
29
 
29
- Cortex agrega sesiones Memory y Redis a agentes multiprovider construidos directamente con
30
- Pydantic AI.
30
+ Cortex agrega sesiones Memory y Redis, junto con capacidades puntuales que Pydantic AI no incluye,
31
+ a agentes multiprovider construidos directamente con Pydantic AI.
31
32
 
32
33
  No implementa otro loop, otra capa de tools ni otra API de agentes. Pydantic AI conserva el control
33
34
  de providers, modelos, tools, tipado, `RunContext`, hooks, límites, approvals, outputs, usage e
34
- historial. Cortex sólo cubre la pieza que Pydantic AI no incluye: persistencia conversacional con un
35
- turno activo por sesión.
35
+ historial. Cortex aporta persistencia conversacional con un turno activo por sesión y capacidades
36
+ opcionales construidas sobre sus hooks públicos.
36
37
 
37
38
  > Cortex Agent SDK está en alfa. La API puede cambiar antes de la versión `1.0.0`.
38
39
 
39
40
  ## Requisitos
40
41
 
41
42
  - Python `>=3.13`.
42
- - Pydantic AI `>=2.27,<3`, con Google y OpenAI instalados por Cortex.
43
+ - Pydantic AI `>=2.27,<3`.
43
44
 
44
45
  ## Instalación
45
46
 
46
- Google, OpenAI y sesiones en memoria:
47
+ Solo sesiones en memoria y capabilities:
47
48
 
48
49
  ```bash
49
50
  uv add cortex-agent-sdk
50
51
  ```
51
52
 
52
- Google, OpenAI y Redis:
53
+ OpenAI y sesiones en memoria:
54
+
55
+ ```bash
56
+ uv add "cortex-agent-sdk[openai]"
57
+ ```
58
+
59
+ OpenAI y Redis:
53
60
 
54
61
  ```bash
55
- uv add "cortex-agent-sdk[redis]"
62
+ uv add "cortex-agent-sdk[openai,redis]"
56
63
  ```
57
64
 
58
- La instalación base incluye Google y OpenAI, además de los endpoints compatibles con OpenAI. Otros
59
- providers pueden agregarse desde los extras oficiales de Pydantic AI cuando un producto realmente
60
- los necesite. Cortex no implementa adapters paralelos.
65
+ Google se instala con el extra `google`. Cada producto elige únicamente sus providers. Cortex no
66
+ implementa adapters paralelos ni ofrece un extra que los instale todos.
61
67
 
62
68
  ## Uso
63
69
 
64
70
  El agente es el `Agent` nativo de Pydantic AI. El store entrega el historial bajo exclusión y lo
65
71
  guarda cuando `session.replace(...)` marca un resultado completo.
72
+ El siguiente ejemplo requiere el extra `openai`.
66
73
 
67
74
  ```python
68
75
  import asyncio
@@ -138,6 +145,25 @@ agent = Agent(model)
138
145
 
139
146
  El context manager de `Agent` administra el transporte del provider.
140
147
 
148
+ ## Fallback del resultado de una tool
149
+
150
+ Pydantic AI reintenta cuando un modelo termina sin texto. Para tools cuyo resultado ya es una
151
+ respuesta completa, Cortex puede reutilizar el último resultado exitoso del mismo run:
152
+
153
+ ```python
154
+ from pydantic_ai import Agent
155
+
156
+ from cortex_agent_sdk.capabilities import last_tool_result_fallback
157
+
158
+ agent = Agent(
159
+ "openai-responses:gpt-5.6-luna",
160
+ capabilities=[last_tool_result_fallback({"confirmar_agenda"})],
161
+ )
162
+ ```
163
+
164
+ La aplicación conserva la decisión sobre las tools elegibles. La capacidad no usa resultados
165
+ fallidos, vacíos ni pertenecientes a otro run, y no reemplaza texto o nuevas llamadas del modelo.
166
+
141
167
  ## Migración desde el runtime anterior
142
168
 
143
169
  | Antes | Ahora |
@@ -151,7 +177,7 @@ El context manager de `Agent` administra el transporte del provider.
151
177
  | `AgentHooks` | `pydantic_ai.capabilities.Hooks` |
152
178
  | `turn_finished` | `Hooks(after_run=...)` |
153
179
  | `history_transform` | `Hooks(before_model_request=...)` |
154
- | `fallback_answer` | política local del producto sobre `AgentRunResult` |
180
+ | `fallback_answer` | `last_tool_result_fallback(...)` opcional |
155
181
  | `AgentOptions` | `UsageLimits`, settings del modelo y argumentos de `Agent` |
156
182
  | `AgentResult.text` | `AgentRunResult.output` |
157
183
  | `SessionStore.acquire` | `SessionStore.turn` + `Session.replace` |
@@ -166,6 +192,7 @@ Pydantic AI.
166
192
  - `cortex_agent_sdk.sessions.SessionStore`
167
193
  - `cortex_agent_sdk.sessions.MemorySessionStore`
168
194
  - `cortex_agent_sdk.redis.RedisSessionStore`
195
+ - `cortex_agent_sdk.capabilities.last_tool_result_fallback`
169
196
  - `cortex_agent_sdk.errores.AppError`
170
197
  - `cortex_agent_sdk.errores.CodigoError`
171
198
  - `cortex_agent_sdk.errores.Severidad`
@@ -1,42 +1,48 @@
1
1
  # Cortex Agent SDK
2
2
 
3
- Cortex agrega sesiones Memory y Redis a agentes multiprovider construidos directamente con
4
- 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.
5
5
 
6
6
  No implementa otro loop, otra capa de tools ni otra API de agentes. Pydantic AI conserva el control
7
7
  de providers, modelos, tools, tipado, `RunContext`, hooks, límites, approvals, outputs, usage e
8
- historial. Cortex sólo cubre la pieza que Pydantic AI no incluye: persistencia conversacional con un
9
- 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.
10
10
 
11
11
  > Cortex Agent SDK está en alfa. La API puede cambiar antes de la versión `1.0.0`.
12
12
 
13
13
  ## Requisitos
14
14
 
15
15
  - Python `>=3.13`.
16
- - Pydantic AI `>=2.27,<3`, con Google y OpenAI instalados por Cortex.
16
+ - Pydantic AI `>=2.27,<3`.
17
17
 
18
18
  ## Instalación
19
19
 
20
- Google, OpenAI y sesiones en memoria:
20
+ Solo sesiones en memoria y capabilities:
21
21
 
22
22
  ```bash
23
23
  uv add cortex-agent-sdk
24
24
  ```
25
25
 
26
- Google, OpenAI y Redis:
26
+ OpenAI y sesiones en memoria:
27
+
28
+ ```bash
29
+ uv add "cortex-agent-sdk[openai]"
30
+ ```
31
+
32
+ OpenAI y Redis:
27
33
 
28
34
  ```bash
29
- uv add "cortex-agent-sdk[redis]"
35
+ uv add "cortex-agent-sdk[openai,redis]"
30
36
  ```
31
37
 
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.
38
+ Google se instala con el extra `google`. Cada producto elige únicamente sus providers. Cortex no
39
+ implementa adapters paralelos ni ofrece un extra que los instale todos.
35
40
 
36
41
  ## Uso
37
42
 
38
43
  El agente es el `Agent` nativo de Pydantic AI. El store entrega el historial bajo exclusión y lo
39
44
  guarda cuando `session.replace(...)` marca un resultado completo.
45
+ El siguiente ejemplo requiere el extra `openai`.
40
46
 
41
47
  ```python
42
48
  import asyncio
@@ -112,6 +118,25 @@ agent = Agent(model)
112
118
 
113
119
  El context manager de `Agent` administra el transporte del provider.
114
120
 
121
+ ## Fallback del resultado de una tool
122
+
123
+ Pydantic AI reintenta cuando un modelo termina sin texto. Para tools cuyo resultado ya es una
124
+ respuesta completa, Cortex puede reutilizar el último resultado exitoso del mismo run:
125
+
126
+ ```python
127
+ from pydantic_ai import Agent
128
+
129
+ from cortex_agent_sdk.capabilities import last_tool_result_fallback
130
+
131
+ agent = Agent(
132
+ "openai-responses:gpt-5.6-luna",
133
+ capabilities=[last_tool_result_fallback({"confirmar_agenda"})],
134
+ )
135
+ ```
136
+
137
+ La aplicación conserva la decisión sobre las tools elegibles. La capacidad no usa resultados
138
+ fallidos, vacíos ni pertenecientes a otro run, y no reemplaza texto o nuevas llamadas del modelo.
139
+
115
140
  ## Migración desde el runtime anterior
116
141
 
117
142
  | Antes | Ahora |
@@ -125,7 +150,7 @@ El context manager de `Agent` administra el transporte del provider.
125
150
  | `AgentHooks` | `pydantic_ai.capabilities.Hooks` |
126
151
  | `turn_finished` | `Hooks(after_run=...)` |
127
152
  | `history_transform` | `Hooks(before_model_request=...)` |
128
- | `fallback_answer` | política local del producto sobre `AgentRunResult` |
153
+ | `fallback_answer` | `last_tool_result_fallback(...)` opcional |
129
154
  | `AgentOptions` | `UsageLimits`, settings del modelo y argumentos de `Agent` |
130
155
  | `AgentResult.text` | `AgentRunResult.output` |
131
156
  | `SessionStore.acquire` | `SessionStore.turn` + `Session.replace` |
@@ -140,6 +165,7 @@ Pydantic AI.
140
165
  - `cortex_agent_sdk.sessions.SessionStore`
141
166
  - `cortex_agent_sdk.sessions.MemorySessionStore`
142
167
  - `cortex_agent_sdk.redis.RedisSessionStore`
168
+ - `cortex_agent_sdk.capabilities.last_tool_result_fallback`
143
169
  - `cortex_agent_sdk.errores.AppError`
144
170
  - `cortex_agent_sdk.errores.CodigoError`
145
171
  - `cortex_agent_sdk.errores.Severidad`
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "cortex-agent-sdk"
3
- version = "0.2.1"
4
- description = "Sesiones Memory y Redis para agentes multiprovider con Pydantic AI"
3
+ version = "0.3.1"
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[google,openai]>=2.27,<3",
20
+ "pydantic-ai-slim>=2.27,<3",
21
21
  ]
22
22
 
23
23
  [project.urls]
@@ -25,9 +25,11 @@ 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
- all = [
30
- "redis>=8.1.0",
28
+ google = [
29
+ "pydantic-ai-slim[google]>=2.27,<3",
30
+ ]
31
+ openai = [
32
+ "pydantic-ai-slim[openai]>=2.27,<3",
31
33
  ]
32
34
  redis = [
33
35
  "redis>=8.1.0",
@@ -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