""" 识别任务壳:入队 Redis Stream,Worker HTTP 槽认领后跑占位处理。 本文件职责:enqueue / process_stub;正式识别(附件/OCR/字段)后续按 payload.kind 分支。 禁止:回调线程同步等识别完成;禁止本模块写主账六态。 """ from __future__ import annotations import logging from dataclasses import dataclass from typing import Any, Optional from agent.redis_coord import get_redis_runtime, try_acquire_idempotency from agent.redis_coord.runtime import RedisRuntime from agent.redis_coord.stream_queue import StreamQueue logger = logging.getLogger(__name__) @dataclass class RecognitionEnqueueResult: accepted: bool entry_id: str = "" reason: str = "" def recognition_stream(redis: Optional[RedisRuntime] = None) -> StreamQueue: """返回识别 Stream 句柄。""" rt = redis or get_redis_runtime() return rt.recognition_stream def enqueue_recognition( *, kind: str, payload: dict[str, Any], idempotency_key: str = "", redis: Optional[RedisRuntime] = None, ) -> RecognitionEnqueueResult: """ 识别任务入队(快回)。 kind 例:attachment_text / vision / sheet_map。 """ rt = redis or get_redis_runtime() if idempotency_key: ok = try_acquire_idempotency( rt.client, scope="recognition", idempotency_key=idempotency_key, ttl_seconds=600, ) if not ok: return RecognitionEnqueueResult(accepted=False, reason="duplicate_idempotency_key") body = {"kind": kind, "payload": payload} entry_id = rt.recognition_stream.enqueue( payload=body, idempotency_key=idempotency_key, ) logger.info("recognition.enqueue kind=%s id=%s", kind, entry_id) return RecognitionEnqueueResult(accepted=True, entry_id=entry_id) def process_recognition_stub(data: dict[str, Any]) -> dict[str, Any]: """ 识别占位:回显 kind,不读附件、不调模型。 Worker 槽调用;正式实现替换为本函数旁的真实处理器。 """ kind = str(data.get("kind") or "unknown") payload = data.get("payload") or {} logger.info("recognition.stub kind=%s keys=%s", kind, sorted(payload.keys()) if isinstance(payload, dict) else []) return { "ok": True, "stub": True, "kind": kind, "note": "识别壳:未跑真实抽取", }