# Pipeline Configuration Schema
# Validates pipeline.config.yaml files generated by aiwg nlp new

$schema: "https://json-schema.org/draft/2020-12/schema"
$id: "https://aiwg.io/schemas/nlp-prod/pipeline-config/v1"
title: "NLP Pipeline Configuration"
description: |
  Schema for pipeline.config.yaml — the declarative specification of an
  LLM inference pipeline. Generated by `aiwg nlp new` and consumed by
  the pipeline runtime stub and eval harness.

type: object
required:
  - version
  - name
  - pattern
  - language
  - steps

properties:
  version:
    type: string
    pattern: "^\\d+\\.\\d+\\.\\d+$"
    default: "1.0.0"
    description: "Config schema version"

  name:
    type: string
    description: "Pipeline name — used as directory name and identifier"
    pattern: "^[a-z][a-z0-9-]*$"

  description:
    type: string
    description: "One-sentence description of what this pipeline does"

  pattern:
    type: string
    enum:
      - simple-chain
      - embedded-agent
      - state-machine
      - rag-pipeline
      - eval-loop
      - dynamic-prompt
    description: "Pipeline pattern — determines runtime behavior and required fields"

  language:
    type: string
    enum: [python, typescript]
    default: python
    description: "Target implementation language"

  framework:
    type: string
    enum: [none, langchain, langgraph]
    default: none
    description: "Optional framework integration (none = clean stub, no dependencies)"

  steps:
    type: array
    minItems: 1
    description: "Ordered list of pipeline steps"
    items:
      $ref: "#/$defs/PipelineStep"

  agent_config:
    $ref: "#/$defs/AgentConfig"
    description: "Required when pattern = embedded-agent"

  fsm_config:
    $ref: "#/$defs/FSMRef"
    description: "Required when pattern = state-machine; points to fsm.config.yaml"

  rag_config:
    $ref: "#/$defs/RAGConfig"
    description: "Required when pattern = rag-pipeline"

  eval_config:
    $ref: "#/$defs/EvalConfig"
    description: "Eval loop configuration"

  cost_config:
    $ref: "#/$defs/CostConfig"
    description: "Cost estimation configuration"

$defs:
  PipelineStep:
    type: object
    required:
      - name
      - prompt
      - model
    properties:
      name:
        type: string
        pattern: "^[a-z][a-z0-9-]*$"
        description: "Step identifier"

      prompt:
        type: string
        description: "Relative path to prompt file (e.g., prompts/extract.prompt.md)"

      model:
        type: string
        enum:
          - claude-haiku-4-5
          - claude-sonnet-4-6
          - claude-opus-4-6
          - gpt-4o
          - gpt-4o-mini
        description: "Model for this step"

      max_tokens:
        type: integer
        minimum: 1
        maximum: 8192
        default: 1024
        description: "Output token cap"

      temperature:
        type: number
        minimum: 0.0
        maximum: 1.0
        default: 0.0
        description: "Sampling temperature (0 = deterministic)"

      input_variables:
        type: array
        items:
          type: string
        description: "Variable names injected into the prompt template"

      output_schema:
        type: string
        description: "Path to JSON Schema file for structured output validation"

      cache_prefix:
        type: boolean
        default: false
        description: "Enable prompt prefix caching for stable system prompt"

      timeout_seconds:
        type: integer
        minimum: 1
        default: 30
        description: "Call timeout"

      retry_config:
        $ref: "#/$defs/RetryConfig"

  AgentConfig:
    type: object
    required:
      - max_iterations
      - exit_conditions
    properties:
      max_iterations:
        type: integer
        minimum: 1
        maximum: 20
        description: "Hard cap on agent iterations"

      tools:
        type: array
        maxItems: 5
        items:
          type: string
        description: "Allowed tool names (max 5 for embedded agent)"

      exit_conditions:
        type: array
        minItems: 1
        items:
          type: string
        description: "Conditions that terminate the agent loop (deterministic exit required)"

      fallback_behavior:
        type: string
        enum: [escalate, return_partial, return_null, raise]
        default: escalate
        description: "What to do when max_iterations reached without exit condition met"

      token_budget:
        type: integer
        description: "Max total tokens for the agent session"

  FSMRef:
    type: object
    required:
      - config_path
    properties:
      config_path:
        type: string
        description: "Relative path to fsm.config.yaml"
        default: "fsm.config.yaml"

  RAGConfig:
    type: object
    required:
      - embedding_model
      - retrieval_k
    properties:
      embedding_model:
        type: string
        enum:
          - text-embedding-3-small
          - text-embedding-3-large
          - voyage-3-large
        default: text-embedding-3-small

      retrieval_k:
        type: integer
        minimum: 1
        maximum: 20
        default: 5
        description: "Number of chunks to retrieve"

      chunk_size:
        type: integer
        minimum: 128
        maximum: 2048
        default: 512

      chunk_overlap:
        type: integer
        minimum: 0
        default: 64

      rerank:
        type: boolean
        default: false
        description: "Enable reranking step after retrieval"

      context_template:
        type: string
        description: "Template for injecting retrieved context into prompt (use {{context}})"

  EvalConfig:
    type: object
    properties:
      enabled:
        type: boolean
        default: true

      evaluator_prompt:
        type: string
        description: "Path to evaluator prompt file (must be separate from generator)"
        default: "prompts/evaluator.prompt.md"

      test_cases:
        type: string
        description: "Path to JSONL test cases file"
        default: "eval/cases.jsonl"

      pass_threshold:
        type: number
        minimum: 0.0
        maximum: 1.0
        default: 0.85

      max_attempts:
        type: integer
        minimum: 1
        maximum: 10
        default: 3

      eval_model:
        type: string
        default: "claude-haiku-4-5"
        description: "Model to use as evaluator (usually cheaper than generator)"

  CostConfig:
    type: object
    properties:
      monthly_volume:
        type: integer
        description: "Expected monthly call volume for cost estimates"

      warn_above_usd:
        type: number
        default: 0.01
        description: "Warn if per-call cost exceeds this threshold"

  RetryConfig:
    type: object
    properties:
      max_attempts:
        type: integer
        minimum: 1
        maximum: 5
        default: 3

      retry_on:
        type: array
        items:
          type: integer
        default: [429, 502, 503]
        description: "HTTP status codes to retry on"

      backoff_seconds:
        type: number
        default: 1.0
        description: "Initial backoff; doubles on each retry"

# Pattern-specific validation notes
validation_notes:
  embedded-agent:
    - "agent_config is required"
    - "agent_config.max_iterations must be set (no unbounded loops)"
    - "agent_config.tools max 5 items"
    - "agent_config.exit_conditions must be non-empty"

  state-machine:
    - "fsm_config is required"
    - "fsm_config.config_path must point to a valid fsm.config.yaml"

  rag-pipeline:
    - "rag_config is required"
    - "steps must include at least one step with {{context}} in prompt"

# Example configs
examples:
  simple_chain:
    version: "1.0.0"
    name: product-extractor
    description: "Extract structured product data from unstructured supplier text"
    pattern: simple-chain
    language: python
    steps:
      - name: extract
        prompt: prompts/extract.prompt.md
        model: claude-haiku-4-5
        max_tokens: 512
        temperature: 0.0
        input_variables: [supplier_text]
        output_schema: schemas/product-output.json
        cache_prefix: true
        timeout_seconds: 15
        retry_config:
          max_attempts: 3
          retry_on: [429, 502, 503]
    eval_config:
      enabled: true
      evaluator_prompt: prompts/evaluator.prompt.md
      test_cases: eval/cases.jsonl
      pass_threshold: 0.9
      max_attempts: 3
      eval_model: claude-haiku-4-5
    cost_config:
      monthly_volume: 100000
      warn_above_usd: 0.001
