Local resource
Pipeline Recipes
_docs/PIPELINE_RECIPES.md
Pipeline Recipes
Recipes define the ordered steps for a flow plus the media policy used by the Temporal workflow.
Active Matrix
| Flow | Default | Recipes |
|---|---|---|
smoke-test | smoke-test-default | smoke-test-default |
initial-ingestion-report | initial-ingestion-report-text-only | initial-ingestion-report-text-only, initial-ingestion-report-text-audio, initial-ingestion-report-all-media |
daily-summary-and-notifications-report | daily-summary-and-notifications-report-text-only | daily-summary-and-notifications-report-text-only, daily-summary-and-notifications-report-text-audio, daily-summary-and-notifications-report-all-media |
interweek-report | interweek-report-default | interweek-report-default |
There are no compatibility aliases. flow is required on every public request.
Recipe Families
flowchart TD
A["FlowRunRequest"] --> B["resolve_flow_recipe"]
B --> N["chat_name_unification"]
N --> C{"recipe media policy"}
C -->|text-only| D["conversation_builder"]
C -->|text-audio| E["Temporal audio fan-out"]
C -->|all-media| F["Temporal audio/image/video/document/link fan-out"]
E --> D
F --> D
D --> G{"flow"}
G -->|smoke-test| H["smoke_test"]
G -->|initial-ingestion-report| I["initial_report"]
I --> J["chat_id_summarization internal batches"]
G -->|daily-summary-and-notifications-report| M["daily_summary_and_notifications"]
B -->|interweek-report| O["interweek_report from explicit context"]
H --> L["response_builder"]
J --> L
M --> L
O --> L
Media Policies
| Recipe suffix | Audio | Image/sticker/GIF | Video | Document | Links |
|---|---|---|---|---|---|
text-only | no | no | no | no | no |
text-audio | yes | no | no | no | no |
all-media | yes | yes | yes | yes | yes |
smoke-test-default is text-only. interweek-report-default is text-only and does not require conversation_builder because it consumes payload.context.
Step Contracts
Step ids and handler names are declarative metadata: they document the pipeline in recipes YAML and tag observability (logs, traces, previews). They are not a runtime dispatch layer — services/recipes/runner.py branches explicitly per flow id and calls the step logic directly. See "Deuda tecnica" below.
| Step | Handler | Input | Output | AI | Prompt |
|---|---|---|---|---|---|
adapter_normalization | waha_adapter | Raw WAHA events or normalized payload. | FlowPayload with conversations, monitored subject, metadata. | no | none |
chat_name_unification | chat_name_unification | FlowPayload.conversations with WAHA chat ids, sender names, and message ids. | Canonical conversations with @lid/@c.us aliases collapsed, names selected, and alias metadata preserved. | no | none |
temporal_media | temporal_media_fanout | FlowPayload, recipe media policy, message media URLs. | media_processed keyed by message id. | media activities may call AI for vision/audio. | prompts/vision.md for visual media; audio transcription has no prompt file. |
conversation_builder | conversation_builder | FlowPayload plus optional media_processed. | One rendered conversation document per real chat_id. | no | none |
smoke_test | smoke_test_generate | Rendered conversation documents. | {"smoke_test": {...}}. | yes | prompts/smoke_test.md |
initial_report | initial_report_generate | Monitored child, generic context without memories/guidance, optional normalized tutor guidance, optional user stats and conversation documents. | {"report": {...}}. | yes | prompts/initial_report.md |
chat_id_summarization | chat_id_summarization | Monitored child minimal identity JSON (name), rendered conversation documents for the current internal batch with matching per-chat memories. | Model-facing arrays chat_id_summaries[] with required chat_name and chat_id_memory_updates[]; response builder returns public dicts keyed by chat_id. | yes | prompts/chat_id_summarization.md |
daily_summary_and_notifications | daily_summary_and_notifications_generate | Monitored child, run metadata, stats, optional normalized tutor guidance, prior report context and day documents with matching per-chat memories. | daily_summary, notifications, model-facing chat_id_memory_updates[]; response builder returns public dict keyed by chat_id. | yes | prompts/daily_summary_and_notifications.md |
interweek_report | interweek_report_generate | Monitored child, run metadata, stats, optional normalized tutor guidance and prior report context. | interweek_report. | yes | prompts/interweek_report.md |
response_builder | response_builder | Step outputs and run metadata. | FlowRunResult; see _docs/OUTPUT_CONTRACTS.md. | no | none |
AI Model Behavior
Every flow AI JSON step uses:
- Primary:
google/gemini-3.5-flash. - Shadow:
google/gemini-3.1-flash-lite. - Temperature:
0. - Response format: strict JSON Schema via OpenRouter structured outputs
- OpenRouter
extra_body.provider.require_parameters=trueis the default - Video and animated image media default to
- OpenRouter
Input tokenscounts prompt text plus the strict JSON Schema.
(response_format.type=json_schema).
fail-fast provider routing policy. Deployments can set OPENROUTER_REQUIRE_PARAMETERS=false when their selected provider/model rejects that option; OpenRouter text, vision, and video requests also set extra_body.preset=main-preset; audio transcription keeps Groq verbose_json.
google/gemini-3.1-flash-lite with MEDIA_RESOLUTION_LOW; static sticker/GIF content remains on vision and the legacy video backend remains selectable.
max_tokens limita output completions only; it does not shrink the input prompt. Preview/debug artifacts include prompt chars and estimated input tokens, and runtime metadata records usage/finish reason when available.
System prompt comes from the active prompt file ## System section. User prompt is constructed from normalized runtime data only. Raw upstream payloads are never sent directly to model prompts.
payload.context.tutor_report_guidance is an optional prompt-only contract. Runtime validates/bounds it and renders valid provided guidance once in each report prompt as a soft preference. It removes the raw key from generic context and never sends it to chat_id_summarization; invalid, omitted or empty input is ignored without failing the flow.
payload.context.monitored_age_band is a second optional prompt-only side contract (wardian-monitored-age-band.v1). With the consumer flag enabled, known 10-12, 13-15 or 16-17 values render once in report prompts for tone/examples/recommendations only. Unknown or invalid input renders nothing; the raw field never reaches generic context, chat summaries, evidence, risk, severity, confidence, taxonomy, alerts or memory updates. DOB/exact age are never model-facing.
Shadow output never overrides primary output. If the shadow call fails, the run continues and stores a shadow failure object.
For initial-ingestion-report, chat_id_summarization is one logical recipe step that can make several model calls through batching interno. CHAT_ID_SUMMARIZATION_BATCH_SIZE defaults to 1 chat document per call and CHAT_ID_SUMMARIZATION_BATCH_MAX_PROMPT_CHARS defaults to 30000 estimated prompt characters, so very large chats can be isolated. Documents are ordered by message volume (desc) before batching. Runtime uses an acumulador en memoria to merge partial outputs after deterministic chat-id verification, splits invalid or mismatched multi-chat batches into single-chat retries, retries single-chat calls up to CHAT_ID_SUMMARIZATION_MAX_RETRIES (default 2), and normalizes an invented id only when one input chat maps to one summary and one memory update after retries. The contrato publico no cambia: output.chat_id_summaries and output.chat_id_memory_updates remain dicts keyed by chat_id.
Deuda tecnica
Pendientes conocidos, en orden de impacto:
- Runner Step/Handler refactor: el runner ejecuta cada flow con branches
- Unificacion YAML <-> Python de recipes:
config/recipes/*.yamly - Doble corpus en initial-ingestion:
initial_reporty
hardcodeados por flow id; los handlers declarados en recipes/YAML no despachan nada en runtime. Refactor pendiente hacia un dispatch real Step -> Handler.
services/flows.py (RECIPE_DEFS) duplican la definicion de cada recipe y se mantienen sincronizados solo por tests.
chat_id_summarization reciben ambos las transcripciones completas. Rediseno futuro: generar el reporte a partir de los summaries por chat en vez de reenviar el corpus completo.