forge-lcdl

Client API (`LcdlClient`)

High-level entrypoint for policy-driven runs: contract validation, optional RAG (retriever + evidence pack), ModelRouter model selection, verification, and limited repair retries — without replacing run_task for…

Client API (LcdlClient)

High-level entrypoint for policy-driven runs: contract validation, optional RAG (retriever + evidence pack), ModelRouter model selection, verification, and limited repair retries — without replacing run_task for low-level or custom flows.

Canonical types live in forge_lcdl.execution and are re-exported from forge_lcdl.

When to use which API

Situation Prefer
Applications that should not wire RAG, gates, and verification manually LcdlClient.execute
DecisionPack DAGs with per-node policy LcdlClient.execute_graph
Tests, one-off scripts, or custom orchestration run_task / TaskRunner
MCP tools or thin wrappers Either; keep run_task stable for existing callers

ExecutionPolicy

Frozen dataclass (see forge_lcdl.execution.policy). Common fields:

  • inference: off | auto | required — whether to run rag_query_plan (auto) and rag_enough_context_gate when RAG is active.
  • rag: off | auto | on | required — retrieval: off skips; on / required forces query path when a retriever is set; auto uses the planner or heuristics.
  • verification: off | schema | full — post-run verifiers (includes rag.citations when listed on the contract).
  • repair: off | safe_retry — on verification failure, retry the task up to max_attempts.
  • max_attempts, min_confidence, max_context_tokens, preferred_model.

Merge with graph nodes via merge_execution_policy (node policy JSON in DecisionPack).

Example: minimal execute (fake transport)

from forge_lcdl import ExecutionPolicy, LcdlClient
from forge_lcdl.env import LlmEnvProfile
from forge_lcdl.types import ChatResult

def fake_chat(messages, **kwargs):
    return ChatResult(True, '{"yes": true, "confidence": 0.9, "reason": "ok"}')

profile = LlmEnvProfile(
    kind="certificator",
    base_url="http://localhost/v1",
    api_key="k",
    model="m",
    timeout_sec=60,
    ngrok_bypass=False,
    prefer_json_object_mode=False,
)

client = LcdlClient(profile=profile, chat=fake_chat)
result = client.execute(
    "llm_boolean_gate",
    {"facts": {"x": 1}, "question": "Is x positive?"},
    policy=ExecutionPolicy(verification="schema"),
)
assert result.ok
assert result.output["yes"] is True

Example: RAG + answer_from_evidence

Use a Retriever (e.g. KeywordContextRetriever wrapping build_context_pack) and set rag="on" or "required". Initial call may omit evidence on input; the engine injects an evidence payload when the contract allows supports_rag.

from pathlib import Path

from forge_lcdl import ExecutionPolicy, LcdlClient
from forge_lcdl.retrieval import KeywordContextRetriever

repo_root = Path("/path/to/repo").resolve()
retriever = KeywordContextRetriever(repo_root, budget_chars=40_000)

client = LcdlClient(
    profile=profile,
    retriever=retriever,
    chat=your_chat_fn,
    policy=ExecutionPolicy(rag="on", inference="auto", verification="schema"),
)
result = client.execute(
    "answer_from_evidence",
    {"question": "What pattern does this codebase use for LLM calls?"},
)

See RAG.md for modes, failures, and citation rules.

Example: execute_graph

Parses a DecisionPack v2-shaped dict, runs GraphExecutor, and calls ExecutionEngine.execute per node. Optional metadata.context.corpus_id is merged into node inputs when corpus_id is missing.

pack = {
    "draft_pack": {
        "schema_version": 2,
        "pack_id": "demo",
        "start": "gate",
        "cost_tier": 1,
        "metadata": {"context": {"corpus_id": "my-docs"}},
        "nodes": {
            "gate": {
                "type": "kernel",
                "task_id": "llm_boolean_gate",
                "version": "v1",
                "input": {"facts": {}, "question": "Ready?"},
                "depends_on": [],
                "policy": {"rag": "off", "verification": "off"},
            },
            "t": {"type": "terminal", "depends_on": ["gate"], "static_payload": {"done": True}},
        },
    }
}
graph = client.execute_graph(pack, policy=ExecutionPolicy(verification="off"))

See GRAPH.md for DecisionPack shape.

Environment

LcdlClient.from_env() uses read_certificator_profile() (LLM_* / OpenAI-compatible env). Pass retriever and chat the same way as the constructor.

See also