feat: implement local P1 contracts and Mock call flow

This commit is contained in:
2026-09-25 16:38:22 +08:00
parent ef84a0663d
commit 0b4b901d85
174 changed files with 18044 additions and 1740 deletions
@@ -0,0 +1,44 @@
{
"schema_version": "call-result.v0.1-proposal",
"event_id": "call-result-invalid-001",
"event_type": "call.result",
"dispatcher_id": "c046b893-8628-4589-ae50-619d049248a6",
"tenant_id": "tenant-a",
"tenant_key": "tenant-a",
"trace_id": "trace-v2",
"occurred_at": "2026-09-18T10:10:15+08:00",
"aggregate_type": "call",
"aggregate_id": "call-a",
"aggregate_version": 1,
"payload": {
"source_command_id": "command-a",
"execution_id": "execution-a",
"call_id": "call-a",
"task_id": "task-a",
"task_revision": 1,
"agent_version_id": "version-a",
"route_policy_id": "route-a",
"caller_profile_id": "caller-a",
"callee": "15003164745",
"trunk_id": "trunk-a",
"started_at": "2026-09-18T10:00:00+08:00",
"ended_at": "2026-09-18T10:10:00+08:00",
"duration_ms": 600000,
"outcome": "answered",
"reason_code": null,
"transcript": [],
"opt_out": false,
"recording": {
"status": "uploaded",
"recording_id": "recording-a",
"upload_id": "upload-a",
"bucket": "example-bucket",
"object_key": "calls/tenant-a/call-a.wav",
"format": "wav",
"channels": 1,
"sample_rate_hz": 8000,
"duration_ms": 600000,
"size_bytes": 9600000
}
}
}
@@ -0,0 +1,54 @@
{
"schema_version": "call-result.v0.1-proposal",
"event_id": "call-result-001",
"event_type": "call.result",
"dispatcher_id": "c046b893-8628-4589-ae50-619d049248a6",
"tenant_id": "tenant-a",
"tenant_key": "tenant-a",
"trace_id": "trace-v2",
"occurred_at": "2026-09-18T10:10:15+08:00",
"aggregate_type": "call",
"aggregate_id": "call-a",
"aggregate_version": 1,
"payload": {
"source_command_id": "command-a",
"execution_id": "execution-a",
"call_id": "call-a",
"task_id": "task-a",
"task_revision": 1,
"agent_version_id": "version-a",
"route_policy_id": "route-a",
"caller_profile_id": "caller-a",
"callee": "15003164745",
"trunk_id": "trunk-a",
"started_at": "2026-09-18T10:00:00+08:00",
"ended_at": "2026-09-18T10:10:00+08:00",
"duration_ms": 600000,
"outcome": "answered",
"reason_code": null,
"transcript": [
{
"turn_id": "turn-1",
"segment_id": "segment-1",
"role": "user",
"text": "示例转写内容",
"start_ms": 1000,
"end_ms": 2500
}
],
"opt_out": false,
"recording": {
"status": "uploaded",
"recording_id": "recording-a",
"upload_id": "upload-a",
"bucket": "example-bucket",
"object_key": "calls/tenant-a/call-a.wav",
"format": "wav",
"channels": 1,
"sample_rate_hz": 8000,
"duration_ms": 600000,
"size_bytes": 9600000,
"checksum_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}
}
}
@@ -0,0 +1,18 @@
{
"schema_version": "command-next.v0.1-proposal",
"dispatcher_id": "c046b893-8628-4589-ae50-619d049248a6",
"tenant_id": "tenant-a",
"tenant_key": "tenant-a",
"trace_id": "trace-v2",
"issued_at": "2026-09-21T00:00:00Z",
"not_after": "2026-09-21T00:00:30Z",
"command_id": "control-must-not-have-id",
"expected_task_revision": 2,
"command_type": "task.control",
"payload": {
"task_id": "task-a",
"action": "pause",
"active_call_policy": "drain",
"reason": "local-test"
}
}
@@ -2,7 +2,7 @@
"schema_version": "config-read.v0.1",
"resource": "error",
"error": {
"code": "not_assigned",
"code": "resource_not_found",
"message": "Task is not assigned to this Dispatcher."
}
}
@@ -0,0 +1,71 @@
{
"fixture_version": "config-read-http-statuses.v0.1",
"responses": [
{
"status": 400,
"body": {
"schema_version": "config-read.v0.1",
"resource": "error",
"error": {
"code": "invalid_request",
"message": "Request parameters are invalid."
}
}
},
{
"status": 401,
"body": {
"schema_version": "config-read.v0.1",
"resource": "error",
"error": {
"code": "unauthorized",
"message": "Dispatcher credentials are invalid."
}
}
},
{
"status": 403,
"body": {
"schema_version": "config-read.v0.1",
"resource": "error",
"error": {
"code": "dispatcher_not_authorized",
"message": "Dispatcher is not authorized for this resource."
}
}
},
{
"status": 404,
"body": {
"schema_version": "config-read.v0.1",
"resource": "error",
"error": {
"code": "resource_not_found",
"message": "The requested task or configuration resource was not found."
}
}
},
{
"status": 503,
"body": {
"schema_version": "config-read.v0.1",
"resource": "error",
"error": {
"code": "tenant_quota_unavailable",
"message": "A current tenant quota is unavailable."
}
}
},
{
"status": 503,
"body": {
"schema_version": "config-read.v0.1",
"resource": "error",
"error": {
"code": "service_unavailable",
"message": "Configuration service is temporarily unavailable."
}
}
}
]
}
@@ -0,0 +1,9 @@
{
"schema_version": "config-read.v0.1",
"resource": "error",
"error": {
"code": "resource_not_found",
"message": "Task is not assigned to this Dispatcher."
},
"unexpected": true
}
@@ -0,0 +1,6 @@
{
"schema_version": "local-mock-recording-failure.v0.1",
"upload_id": "upload-a",
"recording_id": "recording-a",
"error_code": "upload_authorization_expired"
}
@@ -0,0 +1,7 @@
{
"schema_version": "local-mock-recording-failure.v0.1",
"upload_id": "upload-a",
"recording_id": "recording-a",
"error_code": "upload_failed",
"put_url": "https://example.invalid/never-a-real-token"
}
@@ -0,0 +1,6 @@
{
"schema_version": "local-mock-recording-failure.v0.1",
"upload_id": "upload-a",
"recording_id": "recording-a",
"error_code": "upload_timeout"
}
@@ -0,0 +1,6 @@
{
"schema_version": "local-mock-recording-failure.v0.1",
"upload_id": "upload-a",
"recording_id": "recording-a",
"error_code": "upload_failed"
}
@@ -0,0 +1,32 @@
{
"schema_version": "task-discovery.v0.2-proposal",
"dispatcher_id": "c046b893-8628-4589-ae50-619d049248a6",
"next_cursor": "opaque-watermark-004",
"changes": [
{
"cursor": "opaque-watermark-002",
"operation": "assigned",
"task_id": "task-new",
"tenant_id": "tenant-id-mock",
"tenant_key": "tenant-mock",
"status": "running",
"task_revision": 1
},
{
"cursor": "opaque-watermark-003",
"operation": "updated",
"task_id": "task-mock",
"tenant_id": "tenant-id-mock",
"tenant_key": "tenant-mock",
"status": "stopped",
"task_revision": 3
},
{
"cursor": "opaque-watermark-004",
"operation": "removed",
"task_id": "task-old",
"tenant_id": "tenant-id-mock",
"tenant_key": "tenant-mock"
}
]
}
@@ -0,0 +1,82 @@
{
"fixture_version": "task-discovery-http-statuses.v0.1",
"responses": [
{
"status": 400,
"body": {
"schema_version": "task-discovery.v0.1-proposal",
"resource": "error",
"error": {
"code": "invalid_cursor",
"message": "The cursor is invalid."
}
}
},
{
"status": 400,
"body": {
"schema_version": "task-discovery.v0.1-proposal",
"resource": "error",
"error": {
"code": "invalid_page_token",
"message": "The page token is invalid."
}
}
},
{
"status": 401,
"body": {
"schema_version": "task-discovery.v0.1-proposal",
"resource": "error",
"error": {
"code": "unauthorized",
"message": "Dispatcher credentials are invalid."
}
}
},
{
"status": 403,
"body": {
"schema_version": "task-discovery.v0.1-proposal",
"resource": "error",
"error": {
"code": "dispatcher_not_authorized",
"message": "Dispatcher is not authorized for this resource."
}
}
},
{
"status": 410,
"body": {
"schema_version": "task-discovery.v0.1-proposal",
"resource": "error",
"error": {
"code": "cursor_expired",
"message": "The cursor expired; a full snapshot is required."
}
}
},
{
"status": 410,
"body": {
"schema_version": "task-discovery.v0.1-proposal",
"resource": "error",
"error": {
"code": "snapshot_expired",
"message": "The snapshot expired; a full snapshot is required."
}
}
},
{
"status": 503,
"body": {
"schema_version": "task-discovery.v0.1-proposal",
"resource": "error",
"error": {
"code": "service_unavailable",
"message": "Task discovery is temporarily unavailable."
}
}
}
]
}
@@ -0,0 +1,10 @@
{
"fixture_version": "task-discovery-http-statuses.v0.2",
"responses": [
{"status": 400, "body": {"schema_version": "task-discovery.v0.2-proposal", "resource": "error", "error": {"code": "invalid_cursor", "message": "The cursor is invalid."}}},
{"status": 401, "body": {"schema_version": "task-discovery.v0.2-proposal", "resource": "error", "error": {"code": "unauthorized", "message": "Dispatcher credentials are invalid."}}},
{"status": 403, "body": {"schema_version": "task-discovery.v0.2-proposal", "resource": "error", "error": {"code": "dispatcher_not_authorized", "message": "Dispatcher is not authorized for this resource."}}},
{"status": 410, "body": {"schema_version": "task-discovery.v0.2-proposal", "resource": "error", "error": {"code": "cursor_expired", "message": "A full task snapshot is required."}}},
{"status": 503, "body": {"schema_version": "task-discovery.v0.2-proposal", "resource": "error", "error": {"code": "service_unavailable", "message": "Task discovery is temporarily unavailable."}}}
]
}
@@ -0,0 +1,7 @@
{
"schema_version": "task-discovery.v0.2-proposal",
"dispatcher_id": "c046b893-8628-4589-ae50-619d049248a6",
"next_cursor": "opaque-watermark-004",
"changes": [],
"next_page_token": "obsolete"
}
@@ -0,0 +1,24 @@
{
"schema_version": "task-discovery.v0.1-proposal",
"dispatcher_id": "c046b893-8628-4589-ae50-619d049248a6",
"mode": "snapshot",
"snapshot_id": "snapshot-1042",
"cursor": "1042",
"tasks": [
{
"task_id": "task-a",
"tenant_id": "tenant-a",
"tenant_key": "tenant-a",
"status": "running",
"task_revision": 1,
"queue": {
"exchange": "agent-call.dispatchers.v3",
"routing_key": "d.c046b893-8628-4589-ae50-619d049248a6.task.task-a.in",
"binding_key": "d.c046b893-8628-4589-ae50-619d049248a6.task.task-a.in",
"queue_name": "agent-call.d.c046b893-8628-4589-ae50-619d049248a6.task.task-a.v3",
"unexpected": true
}
}
],
"next_page_token": null
}
@@ -0,0 +1,13 @@
{
"schema_version": "task-discovery.v0.2-proposal",
"dispatcher_id": "c046b893-8628-4589-ae50-619d049248a6",
"cursor": "opaque-watermark-001",
"tasks": [{
"task_id": "task-mock",
"tenant_id": "tenant-id-mock",
"tenant_key": "tenant-mock",
"status": "running",
"task_revision": 2,
"queue": {"queue_name": "not-allowed-in-response"}
}]
}
@@ -0,0 +1,6 @@
{
"schema_version": "task-discovery.v0.2-proposal",
"dispatcher_id": "c046b893-8628-4589-ae50-619d049248a6",
"next_cursor": "opaque-watermark-004",
"changes": []
}
@@ -0,0 +1,23 @@
{
"schema_version": "task-discovery.v0.1-proposal",
"dispatcher_id": "c046b893-8628-4589-ae50-619d049248a6",
"mode": "snapshot",
"snapshot_id": "snapshot-001",
"cursor": "change-watermark-001",
"tasks": [
{
"task_id": "task-mock",
"tenant_id": "tenant-id-mock",
"tenant_key": "tenant-mock",
"status": "running",
"task_revision": 2,
"queue": {
"exchange": "agent-call.dispatchers.v3",
"routing_key": "d.c046b893-8628-4589-ae50-619d049248a6.task.task-mock.in",
"binding_key": "d.c046b893-8628-4589-ae50-619d049248a6.task.task-mock.in",
"queue_name": "agent-call.d.c046b893-8628-4589-ae50-619d049248a6.task.task-mock.v3"
}
}
],
"next_page_token": null
}
@@ -0,0 +1,14 @@
{
"schema_version": "task-discovery.v0.2-proposal",
"dispatcher_id": "c046b893-8628-4589-ae50-619d049248a6",
"cursor": "opaque-watermark-001",
"tasks": [
{
"task_id": "task-mock",
"tenant_id": "tenant-id-mock",
"tenant_key": "tenant-mock",
"status": "running",
"task_revision": 2
}
]
}