cortex-agent-sdk 0.0.3__tar.gz → 0.0.4__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 (44) hide show
  1. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/PKG-INFO +43 -2
  2. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/README.md +42 -1
  3. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/pyproject.toml +1 -1
  4. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/tools/contracts.py +136 -23
  5. cortex_agent_sdk-0.0.4/src/cortex_agent_sdk/tools/decorators.py +65 -0
  6. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/tools/execution.py +6 -1
  7. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/tools/models.py +2 -1
  8. cortex_agent_sdk-0.0.3/src/cortex_agent_sdk/tools/decorators.py +0 -15
  9. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/.gitignore +0 -0
  10. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/LICENSE +0 -0
  11. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/__init__.py +0 -0
  12. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/agent.py +0 -0
  13. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/engine.py +0 -0
  14. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/errores/__init__.py +0 -0
  15. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/errores/catalogo.py +0 -0
  16. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/errores/excepcion.py +0 -0
  17. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/gateway.py +0 -0
  18. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/google/__init__.py +0 -0
  19. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/history/__init__.py +0 -0
  20. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/history/models.py +0 -0
  21. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/history/pipeline.py +0 -0
  22. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/history/transform.py +0 -0
  23. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/hooks.py +0 -0
  24. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/immutable.py +0 -0
  25. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/lifecycle.py +0 -0
  26. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/openai/__init__.py +0 -0
  27. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/openai/engine.py +0 -0
  28. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/openai/models.py +0 -0
  29. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/openai/options.py +0 -0
  30. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/postgres/__init__.py +0 -0
  31. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/postgres/store.py +0 -0
  32. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/py.typed +0 -0
  33. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/redis/__init__.py +0 -0
  34. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/redis/scripts.py +0 -0
  35. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/redis/store.py +0 -0
  36. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/results.py +0 -0
  37. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/runtime.py +0 -0
  38. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/sessions/__init__.py +0 -0
  39. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/sessions/codec.py +0 -0
  40. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/sessions/lease.py +0 -0
  41. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/sessions/memory.py +0 -0
  42. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/sessions/models.py +0 -0
  43. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/sessions/store.py +0 -0
  44. {cortex_agent_sdk-0.0.3 → cortex_agent_sdk-0.0.4}/src/cortex_agent_sdk/tools/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cortex-agent-sdk
3
- Version: 0.0.3
3
+ Version: 0.0.4
4
4
  Summary: SDK async y multiproveedor para construir agentes con control explícito
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
@@ -137,10 +137,51 @@ async def main() -> None:
137
137
  asyncio.run(main())
138
138
  ```
139
139
 
140
+ Los type hints son la vía recomendada para tools sencillas. Cortex infiere el contrato visible y
141
+ valida los argumentos antes de ejecutar la función; no hace falta escribir JSON Schema ni
142
+ `ToolSpec` manualmente.
143
+
144
+ ### Contratos Pydantic
145
+
146
+ Cuando los argumentos forman un contrato reutilizable o tienen validaciones entre campos, puede
147
+ usarse un `BaseModel` como fuente de verdad:
148
+
149
+ ```python
150
+ from datetime import datetime
151
+ from typing import Self
152
+
153
+ from pydantic import BaseModel, Field, model_validator
154
+
155
+ from cortex_agent_sdk import final_answer
156
+
157
+
158
+ class ReminderArgs(BaseModel):
159
+ message: str = Field(min_length=1, description="Texto del recordatorio.")
160
+ when: datetime | None = None
161
+ cron: str | None = None
162
+
163
+ @model_validator(mode="after")
164
+ def validate_mode(self) -> Self:
165
+ if (self.when is None) == (self.cron is None):
166
+ raise ValueError("se requiere exactamente when o cron")
167
+ return self
168
+
169
+
170
+ @final_answer(input_model=ReminderArgs)
171
+ async def schedule_reminder(args: ReminderArgs) -> str:
172
+ """Programa un recordatorio."""
173
+ return f"Recordatorio: {args.message}"
174
+ ```
175
+
176
+ Cortex genera el `ToolSpec` neutral a partir del modelo, rechaza propiedades adicionales y entrega a
177
+ la función una instancia ya validada. `ToolBinding(input_model=...)` ofrece la misma capacidad para
178
+ tools construidas en runtime. El `ToolSpec` manual continúa disponible como escape hatch cuando el
179
+ schema visible necesita construirse dinámicamente o requiere control de bajo nivel.
180
+
140
181
  ## Capacidades del alfa
141
182
 
142
183
  - Loop async acotado.
143
- - Tools async con schema inferido o explícito.
184
+ - Tools async con schema inferido, Pydantic explícito o `ToolSpec` manual.
144
185
  - Historial y sesiones en memoria, Redis o PostgreSQL.
145
186
  - Hooks locales.
146
187
  - Timeouts para providers y tools.
@@ -103,10 +103,51 @@ async def main() -> None:
103
103
  asyncio.run(main())
104
104
  ```
105
105
 
106
+ Los type hints son la vía recomendada para tools sencillas. Cortex infiere el contrato visible y
107
+ valida los argumentos antes de ejecutar la función; no hace falta escribir JSON Schema ni
108
+ `ToolSpec` manualmente.
109
+
110
+ ### Contratos Pydantic
111
+
112
+ Cuando los argumentos forman un contrato reutilizable o tienen validaciones entre campos, puede
113
+ usarse un `BaseModel` como fuente de verdad:
114
+
115
+ ```python
116
+ from datetime import datetime
117
+ from typing import Self
118
+
119
+ from pydantic import BaseModel, Field, model_validator
120
+
121
+ from cortex_agent_sdk import final_answer
122
+
123
+
124
+ class ReminderArgs(BaseModel):
125
+ message: str = Field(min_length=1, description="Texto del recordatorio.")
126
+ when: datetime | None = None
127
+ cron: str | None = None
128
+
129
+ @model_validator(mode="after")
130
+ def validate_mode(self) -> Self:
131
+ if (self.when is None) == (self.cron is None):
132
+ raise ValueError("se requiere exactamente when o cron")
133
+ return self
134
+
135
+
136
+ @final_answer(input_model=ReminderArgs)
137
+ async def schedule_reminder(args: ReminderArgs) -> str:
138
+ """Programa un recordatorio."""
139
+ return f"Recordatorio: {args.message}"
140
+ ```
141
+
142
+ Cortex genera el `ToolSpec` neutral a partir del modelo, rechaza propiedades adicionales y entrega a
143
+ la función una instancia ya validada. `ToolBinding(input_model=...)` ofrece la misma capacidad para
144
+ tools construidas en runtime. El `ToolSpec` manual continúa disponible como escape hatch cuando el
145
+ schema visible necesita construirse dinámicamente o requiere control de bajo nivel.
146
+
106
147
  ## Capacidades del alfa
107
148
 
108
149
  - Loop async acotado.
109
- - Tools async con schema inferido o explícito.
150
+ - Tools async con schema inferido, Pydantic explícito o `ToolSpec` manual.
110
151
  - Historial y sesiones en memoria, Redis o PostgreSQL.
111
152
  - Hooks locales.
112
153
  - Timeouts para providers y tools.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cortex-agent-sdk"
3
- version = "0.0.3"
3
+ version = "0.0.4"
4
4
  description = "SDK async y multiproveedor para construir agentes con control explícito"
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -21,6 +21,7 @@ class ToolDefinition:
21
21
  private_arguments: Mapping[str, object]
22
22
  is_final_answer: bool
23
23
  schema_validator: Draft202012Validator | None
24
+ input_parameter: str | None = None
24
25
 
25
26
 
26
27
  def build_definition(item: ToolFunction | ToolBinding) -> ToolDefinition:
@@ -38,42 +39,68 @@ def build_definition(item: ToolFunction | ToolBinding) -> ToolDefinition:
38
39
  if not inspect.iscoroutinefunction(function):
39
40
  raise AppError(CodigoError.CONFIG_INVALIDA, f"{function.__name__} debe ser async")
40
41
 
41
- input_model, inferred_spec = _infer_contract(function, binding.private_arguments)
42
+ declared_model = _declared_input_model(binding, function)
43
+ if declared_model is None:
44
+ input_model, inferred_spec = _infer_contract(function, binding.private_arguments)
45
+ input_parameter = None
46
+ else:
47
+ input_model, inferred_spec, input_parameter = _model_contract(
48
+ function,
49
+ declared_model,
50
+ binding.private_arguments,
51
+ )
52
+
42
53
  spec = binding.spec or inferred_spec
43
54
  schema_validator = _explicit_validator(binding.spec, inferred_spec)
44
55
  private_arguments = MappingProxyType(dict(binding.private_arguments))
45
56
  is_final = bool(getattr(function, "__cortex_final_answer__", False))
46
57
  return ToolDefinition(
47
- spec,
48
- binding.function,
49
- input_model,
50
- private_arguments,
51
- is_final,
52
- schema_validator,
58
+ spec=spec,
59
+ function=binding.function,
60
+ input_model=input_model,
61
+ private_arguments=private_arguments,
62
+ is_final_answer=is_final,
63
+ schema_validator=schema_validator,
64
+ input_parameter=input_parameter,
53
65
  )
54
66
 
55
67
 
68
+ def _declared_input_model(
69
+ binding: ToolBinding,
70
+ function: ToolFunction,
71
+ ) -> type[BaseModel] | None:
72
+ decorated = getattr(binding.function, "__cortex_input_model__", None)
73
+ if decorated is None:
74
+ decorated = getattr(function, "__cortex_input_model__", None)
75
+ explicit = binding.input_model
76
+ if explicit is not None and decorated is not None and explicit is not decorated:
77
+ raise AppError(
78
+ CodigoError.CONFIG_INVALIDA,
79
+ f"contratos Pydantic incompatibles en {function.__name__}",
80
+ )
81
+ model = explicit or decorated
82
+ if model is None:
83
+ return None
84
+ if not isinstance(model, type) or not issubclass(model, BaseModel):
85
+ raise AppError(
86
+ CodigoError.CONFIG_INVALIDA,
87
+ f"input_model inválido en {function.__name__}",
88
+ )
89
+ return model
90
+
91
+
56
92
  def _infer_contract(
57
93
  function: ToolFunction,
58
94
  private_arguments: Mapping[str, object],
59
95
  ) -> tuple[type[BaseModel], ToolSpec]:
60
96
  signature = inspect.signature(function)
61
- try:
62
- annotations = get_type_hints(function, include_extras=True)
63
- except (NameError, TypeError) as error:
64
- raise AppError(
65
- CodigoError.CONFIG_INVALIDA,
66
- f"no se resolvieron tipos de {function.__name__}",
67
- ) from error
97
+ annotations = _resolved_annotations(function)
68
98
 
69
99
  fields: dict[str, Any] = {}
70
100
  injected_names: set[str] = set()
71
101
  for name, parameter in signature.parameters.items():
72
102
  annotation = annotations.get(name, parameter.annotation)
73
- if annotation is inspect.Signature.empty:
74
- raise AppError(CodigoError.CONFIG_INVALIDA, f"falta tipo en {function.__name__}.{name}")
75
- if parameter.kind in {parameter.VAR_POSITIONAL, parameter.VAR_KEYWORD}:
76
- raise AppError(CodigoError.CONFIG_INVALIDA, f"{function.__name__} no admite variádicos")
103
+ _validate_parameter(function, name, parameter, annotation)
77
104
  if _is_injected(annotation):
78
105
  injected_names.add(name)
79
106
  continue
@@ -82,24 +109,110 @@ def _infer_contract(
82
109
  default = ...
83
110
  fields[name] = (annotation, default)
84
111
 
112
+ _validate_injected(function, injected_names, private_arguments)
113
+
114
+ config = ConfigDict(extra="forbid")
115
+ model_name = f"{function.__name__.title().replace('_', '')}Arguments"
116
+ input_model = create_model(model_name, __config__=config, **fields)
117
+ return input_model, _spec_from_model(function, input_model)
118
+
119
+
120
+ def _model_contract(
121
+ function: ToolFunction,
122
+ input_model: type[BaseModel],
123
+ private_arguments: Mapping[str, object],
124
+ ) -> tuple[type[BaseModel], ToolSpec, str]:
125
+ signature = inspect.signature(function)
126
+ annotations = _resolved_annotations(function)
127
+ public_names: list[str] = []
128
+ injected_names: set[str] = set()
129
+ input_parameter: str | None = None
130
+
131
+ for name, parameter in signature.parameters.items():
132
+ annotation = annotations.get(name, parameter.annotation)
133
+ _validate_parameter(function, name, parameter, annotation)
134
+ if _is_injected(annotation):
135
+ injected_names.add(name)
136
+ continue
137
+ public_names.append(name)
138
+ if _annotation_base(annotation) is input_model:
139
+ input_parameter = name
140
+
141
+ _validate_injected(function, injected_names, private_arguments)
142
+ if len(public_names) != 1 or input_parameter is None:
143
+ raise AppError(
144
+ CodigoError.CONFIG_INVALIDA,
145
+ f"{function.__name__} debe recibir un único argumento público {input_model.__name__}",
146
+ )
147
+ return input_model, _spec_from_model(function, input_model), input_parameter
148
+
149
+
150
+ def _resolved_annotations(function: ToolFunction) -> dict[str, Any]:
151
+ try:
152
+ return get_type_hints(function, include_extras=True)
153
+ except (NameError, TypeError) as error:
154
+ raise AppError(
155
+ CodigoError.CONFIG_INVALIDA,
156
+ f"no se resolvieron tipos de {function.__name__}",
157
+ ) from error
158
+
159
+
160
+ def _validate_parameter(
161
+ function: ToolFunction,
162
+ name: str,
163
+ parameter: inspect.Parameter,
164
+ annotation: object,
165
+ ) -> None:
166
+ if annotation is inspect.Signature.empty:
167
+ raise AppError(CodigoError.CONFIG_INVALIDA, f"falta tipo en {function.__name__}.{name}")
168
+ if parameter.kind in {parameter.VAR_POSITIONAL, parameter.VAR_KEYWORD}:
169
+ raise AppError(CodigoError.CONFIG_INVALIDA, f"{function.__name__} no admite variádicos")
170
+
171
+
172
+ def _validate_injected(
173
+ function: ToolFunction,
174
+ injected_names: set[str],
175
+ private_arguments: Mapping[str, object],
176
+ ) -> None:
85
177
  if injected_names != set(private_arguments):
86
178
  raise AppError(
87
179
  CodigoError.CONFIG_INVALIDA,
88
180
  f"inyección inválida en {function.__name__}",
89
181
  )
90
182
 
91
- config = ConfigDict(extra="forbid")
92
- model_name = f"{function.__name__.title().replace('_', '')}Arguments"
93
- input_model = create_model(model_name, __config__=config, **fields)
183
+
184
+ def _spec_from_model(function: ToolFunction, input_model: type[BaseModel]) -> ToolSpec:
94
185
  schema = input_model.model_json_schema()
95
186
  schema.pop("title", None)
187
+ _forbid_object_extras(schema)
188
+ if schema.get("type") != "object" or not isinstance(schema.get("properties"), dict):
189
+ raise AppError(
190
+ CodigoError.CONFIG_INVALIDA,
191
+ f"{input_model.__name__} debe describir argumentos de tipo object",
192
+ )
96
193
  description = (inspect.getdoc(function) or "").split("\n", maxsplit=1)[0]
97
- spec = ToolSpec(
194
+ return ToolSpec(
98
195
  name=function.__name__,
99
196
  description=description,
100
197
  parameters=schema,
101
198
  )
102
- return input_model, spec
199
+
200
+
201
+ def _forbid_object_extras(value: object) -> None:
202
+ if isinstance(value, dict):
203
+ if value.get("type") == "object" and isinstance(value.get("properties"), dict):
204
+ value["additionalProperties"] = False
205
+ for nested in value.values():
206
+ _forbid_object_extras(nested)
207
+ elif isinstance(value, list):
208
+ for nested in value:
209
+ _forbid_object_extras(nested)
210
+
211
+
212
+ def _annotation_base(annotation: object) -> object:
213
+ if get_origin(annotation) is Annotated:
214
+ return get_args(annotation)[0]
215
+ return annotation
103
216
 
104
217
 
105
218
  def _is_injected(annotation: object) -> bool:
@@ -0,0 +1,65 @@
1
+ from collections.abc import Callable
2
+ from typing import overload
3
+
4
+ from pydantic import BaseModel
5
+
6
+ from cortex_agent_sdk.tools.models import ToolFunction
7
+
8
+
9
+ @overload
10
+ def tool[F: ToolFunction](function: F, /) -> F: ...
11
+
12
+
13
+ @overload
14
+ def tool[F: ToolFunction](
15
+ *,
16
+ input_model: type[BaseModel] | None = None,
17
+ ) -> Callable[[F], F]: ...
18
+
19
+
20
+ def tool(
21
+ function: ToolFunction | None = None,
22
+ *,
23
+ input_model: type[BaseModel] | None = None,
24
+ ) -> ToolFunction | Callable[[ToolFunction], ToolFunction]:
25
+ """Marca una tool y permite declarar un contrato Pydantic explícito."""
26
+
27
+ def decorate(inner: ToolFunction) -> ToolFunction:
28
+ inner.__cortex_tool__ = True # type: ignore[attr-defined]
29
+ if input_model is not None:
30
+ inner.__cortex_input_model__ = input_model # type: ignore[attr-defined]
31
+ return inner
32
+
33
+ if function is None:
34
+ return decorate
35
+ return decorate(function)
36
+
37
+
38
+ @overload
39
+ def final_answer[F: ToolFunction](function: F, /) -> F: ...
40
+
41
+
42
+ @overload
43
+ def final_answer[F: ToolFunction](
44
+ *,
45
+ input_model: type[BaseModel] | None = None,
46
+ ) -> Callable[[F], F]: ...
47
+
48
+
49
+ def final_answer(
50
+ function: ToolFunction | None = None,
51
+ *,
52
+ input_model: type[BaseModel] | None = None,
53
+ ) -> ToolFunction | Callable[[ToolFunction], ToolFunction]:
54
+ """Marca una tool terminal y permite un contrato Pydantic explícito."""
55
+
56
+ def decorate(inner: ToolFunction) -> ToolFunction:
57
+ inner.__cortex_tool__ = True # type: ignore[attr-defined]
58
+ inner.__cortex_final_answer__ = True # type: ignore[attr-defined]
59
+ if input_model is not None:
60
+ inner.__cortex_input_model__ = input_model # type: ignore[attr-defined]
61
+ return inner
62
+
63
+ if function is None:
64
+ return decorate
65
+ return decorate(function)
@@ -142,7 +142,7 @@ def _validate_arguments(
142
142
  definition.is_final_answer,
143
143
  )
144
144
  try:
145
- public_arguments = definition.input_model.model_validate(public_input).model_dump()
145
+ validated = definition.input_model.model_validate(public_input, extra="forbid")
146
146
  except ValidationError:
147
147
  return _failed_outcome(
148
148
  call,
@@ -150,6 +150,11 @@ def _validate_arguments(
150
150
  "Los argumentos de la herramienta son inválidos.",
151
151
  definition.is_final_answer,
152
152
  )
153
+
154
+ if definition.input_parameter is not None:
155
+ public_arguments: dict[str, object] = {definition.input_parameter: validated}
156
+ else:
157
+ public_arguments = validated.model_dump()
153
158
  return {**public_arguments, **definition.private_arguments}
154
159
 
155
160
 
@@ -46,11 +46,12 @@ ToolFunction = Callable[..., Awaitable[object]]
46
46
 
47
47
  @dataclass(frozen=True, slots=True)
48
48
  class ToolBinding:
49
- """Une callable, schema opcional y argumentos privados."""
49
+ """Une callable, contrato opcional y argumentos privados."""
50
50
 
51
51
  function: ToolFunction
52
52
  spec: ToolSpec | None = None
53
53
  private_arguments: Mapping[str, object] = field(default_factory=dict)
54
+ input_model: type[BaseModel] | None = None
54
55
 
55
56
  def __post_init__(self) -> None:
56
57
  frozen_arguments = MappingProxyType(dict(self.private_arguments))
@@ -1,15 +0,0 @@
1
- from cortex_agent_sdk.tools.models import ToolFunction
2
-
3
-
4
- def tool(function: ToolFunction) -> ToolFunction:
5
- """Marca un callable async para registrarlo en un Agent."""
6
- function.__cortex_tool__ = True # type: ignore[attr-defined]
7
- return function
8
-
9
-
10
- def final_answer(function: ToolFunction) -> ToolFunction:
11
- """Marca una tool cuyo str exitoso termina el turno."""
12
- function.__cortex_tool__ = True # type: ignore[attr-defined]
13
- function.__cortex_final_answer__ = True # type: ignore[attr-defined]
14
- return function
15
-