feat: implement local P1 contracts and Mock call flow
This commit is contained in:
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user