feat: sync latest ARR implementation

This commit is contained in:
Wyndham ARR
2026-07-31 15:11:42 +08:00
parent d6f8a747fa
commit bf7939dd1a
185 changed files with 17527 additions and 2260 deletions

View File

@@ -9,7 +9,7 @@ from datetime import date, datetime, timedelta, timezone
from typing import List, Optional, Union
from pathlib import Path
from agent_integration.client import OpenAgentAPIError, OpenAgentTransportError
from agent_integration.client import OpenAgentAPIError, OpenAgentEvent, OpenAgentTransportError
from arr_ingestion.contracts import ArtifactRef, XLSX_MIME
from arr_processing.contracts import (
PROCESSING_REQUEST_VERSION,
@@ -198,8 +198,11 @@ class ProcessingContractTests(unittest.TestCase):
self.assertFalse(program_schema["additionalProperties"])
self.assertEqual(
program_schema["properties"]["contract_version"]["const"],
"arr-opera-daily-program-input-2",
"arr-opera-daily-program-input-3",
)
attachment_oss = program_schema["$defs"]["attachment"]["properties"]["oss"]
self.assertIn("url", attachment_oss["required"])
self.assertEqual(attachment_oss["properties"]["url"]["format"], "uri")
self.assertEqual(
program_schema["properties"]["attachment_fetch_policy"]
["properties"]["tool_name"]["const"],
@@ -243,12 +246,12 @@ class ProcessingContractTests(unittest.TestCase):
ProcessingRequest.from_dict(payload)
def test_oss_program_message_matches_fetch_tool_attachment_contract(self):
value = request(job_id="arrjob-source-message-001")
value = request(job_id="arrjob-source:message-001")
source = ArtifactRef(
role="source_xml",
file_kind="opera_xml",
object_key=(
"arr/jobs/arrjob-source-message-001/attempts/0001/"
"arr/jobs/arrjob-source:message-001/attempts/0001/"
"committed/source_xml/source.xml"
),
original_filename="source.xml",
@@ -299,6 +302,13 @@ class ProcessingContractTests(unittest.TestCase):
"oss-cn-guangzhou.aliyuncs.com",
)
self.assertEqual(attachment["oss"]["object_key"], source.object_key)
self.assertEqual(
attachment["oss"]["url"],
"https://one-feel-bucket.oss-cn-guangzhou.aliyuncs.com/"
"arr/jobs/arrjob-source%3Amessage-001/attempts/0001/"
"committed/source_xml/source.xml",
)
self.assertNotIn("?", attachment["oss"]["url"])
for forbidden in ("access_key", "secret", "signed_url", "local_path"):
self.assertNotIn(forbidden, rendered.lower())
@@ -753,13 +763,19 @@ class ProcessingOutputRegistrarTests(unittest.TestCase):
class FakeOpenAgentService:
def __init__(self, response=None, error: Optional[BaseException] = None) -> None:
def __init__(
self,
response=None,
error: Optional[BaseException] = None,
trace_events: Optional[list[OpenAgentEvent]] = None,
) -> None:
self.response = response or {
"run_id": "run-processing-001",
"status": "running",
}
self.error = error
self.calls = []
self.trace_events = trace_events
def send_message(self, *args, **kwargs):
self.calls.append((args, kwargs))
@@ -767,6 +783,12 @@ class FakeOpenAgentService:
raise self.error
return self.response
def stream_message(self, *args, **kwargs):
self.calls.append((args, kwargs))
if self.error is not None:
raise self.error
yield from self.trace_events or []
def get_run(self, *_args, **_kwargs):
if self.error is not None:
raise self.error
@@ -800,6 +822,113 @@ class OpenAgentProcessingTransportTests(unittest.TestCase):
self.assertNotIn("object_key", rendered)
self.assertNotIn("local_path", rendered)
def test_trace_adapter_returns_from_run_started_then_drains_safe_events(self):
class MemoryTraceStore:
def __init__(self) -> None:
self.records = []
def append(self, job_id, record):
self.records.append((job_id, dict(record)))
service = FakeOpenAgentService(
trace_events=[
OpenAgentEvent(
event="trace",
data={
"event": "run.started",
"run_id": "run-trace-001",
"ts": "2026-07-29T13:00:00Z",
},
),
OpenAgentEvent(
event="trace",
data={
"event": "task.updated",
"run_id": "run-trace-001",
"ts": "2026-07-29T13:00:01Z",
"data": {"summary": "开始处理任务"},
},
),
OpenAgentEvent(
event="trace",
data={
"event": "message.final",
"run_id": "run-trace-001",
"text": "submission_grant=must-not-escape",
},
),
OpenAgentEvent(
event="trace",
data={
"event": "step.updated",
"run_id": "run-trace-001",
"ts": "2026-07-29T13:00:02Z",
"data": {"summary": "工具完成 token=must-not-escape"},
},
),
OpenAgentEvent(
event="trace",
data={
"event": "run.completed",
"run_id": "run-trace-001",
"status": "success",
"ts": "2026-07-29T13:00:03Z",
},
),
OpenAgentEvent(event="end", data=None),
]
)
trace_store = MemoryTraceStore()
transport = OpenAgentProcessingTransport(
service, # type: ignore[arg-type]
trace_store=trace_store,
)
value = request()
snapshot = transport.submit(value, "idem-trace-001")
transport.close(timeout_seconds=1.0)
self.assertEqual(snapshot, RemoteRunSnapshot("run-trace-001", "running"))
args, kwargs = service.calls[0]
self.assertNotIn(value.job_id, args[0])
self.assertEqual(json.loads(args[1]), value.to_dict())
self.assertTrue(kwargs["include_trace"])
codes = [record[1]["code"] for record in trace_store.records]
self.assertEqual(
codes,
[
"AGENT_RUN_STARTED",
"AGENT_TASK_UPDATED",
"AGENT_STEP_UPDATED",
"AGENT_RUN_COMPLETED",
"AGENT_TRACE_STREAM_ENDED",
],
)
rendered = json.dumps(trace_store.records, ensure_ascii=False)
self.assertNotIn("must-not-escape", rendered)
def test_trace_submit_transport_failure_is_not_retried_without_server_deduplication(self):
class MemoryTraceStore:
def append(self, _job_id, _record):
pass
service = FakeOpenAgentService(
error=OpenAgentTransportError("ambiguous after POST")
)
transport = OpenAgentProcessingTransport(
service, # type: ignore[arg-type]
trace_store=MemoryTraceStore(),
)
with self.assertRaises(ProcessingTransportError) as raised:
transport.submit(request(), "idem-trace-ambiguous")
self.assertEqual(
raised.exception.code,
"PROCESSING_REMOTE_SUBMISSION_AMBIGUOUS",
)
self.assertFalse(raised.exception.retryable)
def test_open_agent_409_429_and_timeout_are_normalized(self):
errors = (
(