prefactor-core 0.2.3__tar.gz → 0.2.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.
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/PKG-INFO +1 -1
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/src/prefactor_core/_version.py +1 -1
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/src/prefactor_core/client.py +1 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/src/prefactor_core/managers/agent_instance.py +20 -3
- prefactor_core-0.2.4/tests/test_agent_instance_finish_status.py +104 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/.gitignore +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/README.md +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/examples/agent_e2e.py +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/pyproject.toml +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/src/prefactor_core/__init__.py +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/src/prefactor_core/config.py +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/src/prefactor_core/context_stack.py +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/src/prefactor_core/exceptions.py +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/src/prefactor_core/managers/__init__.py +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/src/prefactor_core/managers/span.py +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/src/prefactor_core/models.py +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/src/prefactor_core/operations.py +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/src/prefactor_core/queue/__init__.py +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/src/prefactor_core/queue/base.py +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/src/prefactor_core/queue/executor.py +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/src/prefactor_core/queue/memory.py +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/src/prefactor_core/schema_registry.py +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/src/prefactor_core/span_context.py +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/src/prefactor_core/utils.py +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/tests/test_client.py +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/tests/test_failure_handling.py +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/tests/test_imports.py +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/tests/test_queue.py +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/tests/test_sdk_header.py +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/tests/test_span_context.py +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/tests/test_span_manager.py +0 -0
- {prefactor_core-0.2.3 → prefactor_core-0.2.4}/tests/test_utils.py +0 -0
|
@@ -274,6 +274,7 @@ class PrefactorCoreClient:
|
|
|
274
274
|
elif operation.type == OperationType.FINISH_AGENT_INSTANCE:
|
|
275
275
|
await self._http.agent_instances.finish(
|
|
276
276
|
agent_instance_id=operation.payload["instance_id"],
|
|
277
|
+
status=operation.payload.get("status", "complete"),
|
|
277
278
|
timestamp=operation.timestamp,
|
|
278
279
|
idempotency_key=operation.payload.get("idempotency_key"),
|
|
279
280
|
)
|
|
@@ -14,6 +14,7 @@ from ..utils import generate_idempotency_key
|
|
|
14
14
|
|
|
15
15
|
if TYPE_CHECKING:
|
|
16
16
|
from prefactor_http.client import PrefactorHttpClient
|
|
17
|
+
from prefactor_http.models.types import FinishStatus
|
|
17
18
|
|
|
18
19
|
from ..client import PrefactorCoreClient
|
|
19
20
|
|
|
@@ -115,20 +116,30 @@ class AgentInstanceManager:
|
|
|
115
116
|
|
|
116
117
|
await self._enqueue(operation)
|
|
117
118
|
|
|
118
|
-
async def finish(
|
|
119
|
+
async def finish(
|
|
120
|
+
self,
|
|
121
|
+
instance_id: str,
|
|
122
|
+
status: "FinishStatus" = "complete",
|
|
123
|
+
) -> None:
|
|
119
124
|
"""Mark an instance as finished.
|
|
120
125
|
|
|
121
126
|
Queues a finish operation for the instance.
|
|
122
127
|
|
|
123
128
|
Args:
|
|
124
129
|
instance_id: The ID of the instance to finish.
|
|
130
|
+
status: Terminal status for the instance. Defaults to ``"complete"``.
|
|
125
131
|
"""
|
|
126
|
-
await self.finish_with_idempotency_key(
|
|
132
|
+
await self.finish_with_idempotency_key(
|
|
133
|
+
instance_id,
|
|
134
|
+
generate_idempotency_key(),
|
|
135
|
+
status=status,
|
|
136
|
+
)
|
|
127
137
|
|
|
128
138
|
async def finish_with_idempotency_key(
|
|
129
139
|
self,
|
|
130
140
|
instance_id: str,
|
|
131
141
|
idempotency_key: str,
|
|
142
|
+
status: "FinishStatus" = "complete",
|
|
132
143
|
) -> None:
|
|
133
144
|
"""Queue a finish operation using a stable idempotency key."""
|
|
134
145
|
operation = Operation(
|
|
@@ -136,6 +147,7 @@ class AgentInstanceManager:
|
|
|
136
147
|
payload={
|
|
137
148
|
"instance_id": instance_id,
|
|
138
149
|
"idempotency_key": idempotency_key,
|
|
150
|
+
"status": status,
|
|
139
151
|
},
|
|
140
152
|
timestamp=datetime.now(timezone.utc),
|
|
141
153
|
)
|
|
@@ -199,16 +211,21 @@ class AgentInstanceHandle:
|
|
|
199
211
|
self._start_idempotency_key,
|
|
200
212
|
)
|
|
201
213
|
|
|
202
|
-
async def finish(self) -> None:
|
|
214
|
+
async def finish(self, status: "FinishStatus" = "complete") -> None:
|
|
203
215
|
"""Mark the instance as finished.
|
|
204
216
|
|
|
205
217
|
This queues a finish operation for the instance.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
status: Terminal status for the instance — one of ``"complete"``,
|
|
221
|
+
``"failed"``, or ``"cancelled"``. Defaults to ``"complete"``.
|
|
206
222
|
"""
|
|
207
223
|
manager = self._client.instance_manager
|
|
208
224
|
assert manager is not None
|
|
209
225
|
await manager.finish_with_idempotency_key(
|
|
210
226
|
self._instance_id,
|
|
211
227
|
self._finish_idempotency_key,
|
|
228
|
+
status=status,
|
|
212
229
|
)
|
|
213
230
|
|
|
214
231
|
async def create_span(
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Tests for AgentInstanceHandle.finish() accepting an optional status.
|
|
2
|
+
|
|
3
|
+
See GitHub issue prefactordev/python-sdk#13.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from types import SimpleNamespace
|
|
9
|
+
from unittest.mock import patch
|
|
10
|
+
|
|
11
|
+
import pytest
|
|
12
|
+
from prefactor_core import PrefactorCoreClient
|
|
13
|
+
from prefactor_core.config import PrefactorCoreConfig, QueueConfig
|
|
14
|
+
from prefactor_http.config import HttpClientConfig
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class _StubAgentInstances:
|
|
18
|
+
def __init__(self) -> None:
|
|
19
|
+
self.finish_calls: list[dict] = []
|
|
20
|
+
|
|
21
|
+
async def register(self, **kwargs):
|
|
22
|
+
return SimpleNamespace(id=kwargs.get("id") or "inst-1")
|
|
23
|
+
|
|
24
|
+
async def start(self, **kwargs):
|
|
25
|
+
return SimpleNamespace(id=kwargs["agent_instance_id"])
|
|
26
|
+
|
|
27
|
+
async def finish(self, **kwargs):
|
|
28
|
+
self.finish_calls.append(kwargs)
|
|
29
|
+
return SimpleNamespace(id=kwargs["agent_instance_id"])
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class _StubAgentSpans:
|
|
33
|
+
async def create(self, **kwargs):
|
|
34
|
+
return SimpleNamespace(id="span-1")
|
|
35
|
+
|
|
36
|
+
async def finish(self, **kwargs):
|
|
37
|
+
return SimpleNamespace(id=kwargs["agent_span_id"])
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class _StubHttpClient:
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
*_args,
|
|
44
|
+
agent_instances: _StubAgentInstances | None = None,
|
|
45
|
+
**_kwargs,
|
|
46
|
+
) -> None:
|
|
47
|
+
self.agent_instances = agent_instances or _StubAgentInstances()
|
|
48
|
+
self.agent_spans = _StubAgentSpans()
|
|
49
|
+
|
|
50
|
+
async def __aenter__(self):
|
|
51
|
+
return self
|
|
52
|
+
|
|
53
|
+
async def __aexit__(self, exc_type, exc, tb):
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _make_client_config() -> PrefactorCoreConfig:
|
|
58
|
+
return PrefactorCoreConfig(
|
|
59
|
+
http_config=HttpClientConfig(
|
|
60
|
+
api_url="https://api.test.com",
|
|
61
|
+
api_token="test-token",
|
|
62
|
+
max_retries=0,
|
|
63
|
+
initial_retry_delay=0.01,
|
|
64
|
+
max_retry_delay=0.02,
|
|
65
|
+
),
|
|
66
|
+
queue_config=QueueConfig(num_workers=1, max_retries=0),
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@pytest.mark.asyncio
|
|
71
|
+
async def test_finish_without_status_defaults_to_complete():
|
|
72
|
+
"""Default call to finish() should forward status='complete' to HTTP."""
|
|
73
|
+
stub_http = _StubHttpClient()
|
|
74
|
+
|
|
75
|
+
with patch("prefactor_core.client.PrefactorHttpClient", return_value=stub_http):
|
|
76
|
+
async with PrefactorCoreClient(_make_client_config()) as client:
|
|
77
|
+
instance = await client.create_agent_instance(
|
|
78
|
+
agent_id="agent-1",
|
|
79
|
+
agent_version={"name": "v1"},
|
|
80
|
+
agent_schema_version={"span_schemas": {}},
|
|
81
|
+
)
|
|
82
|
+
await instance.finish()
|
|
83
|
+
|
|
84
|
+
assert len(stub_http.agent_instances.finish_calls) == 1
|
|
85
|
+
assert stub_http.agent_instances.finish_calls[0].get("status") == "complete"
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@pytest.mark.asyncio
|
|
89
|
+
@pytest.mark.parametrize("status", ["failed", "cancelled", "complete"])
|
|
90
|
+
async def test_finish_forwards_explicit_status(status):
|
|
91
|
+
"""Explicit status on handle.finish() should reach the HTTP layer."""
|
|
92
|
+
stub_http = _StubHttpClient()
|
|
93
|
+
|
|
94
|
+
with patch("prefactor_core.client.PrefactorHttpClient", return_value=stub_http):
|
|
95
|
+
async with PrefactorCoreClient(_make_client_config()) as client:
|
|
96
|
+
instance = await client.create_agent_instance(
|
|
97
|
+
agent_id="agent-1",
|
|
98
|
+
agent_version={"name": "v1"},
|
|
99
|
+
agent_schema_version={"span_schemas": {}},
|
|
100
|
+
)
|
|
101
|
+
await instance.finish(status=status)
|
|
102
|
+
|
|
103
|
+
assert len(stub_http.agent_instances.finish_calls) == 1
|
|
104
|
+
assert stub_http.agent_instances.finish_calls[0]["status"] == status
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|