No description
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-09-02 15:06:44 +02:00
sample_data Simplify prompts, move detailed workflow into skill; add concise-communication guidance 2026-08-31 15:59:47 +02:00
skills/product-import Gate map/upload tools behind product-import skill load in subagent 2026-08-31 16:13:52 +02:00
src/distru_agent system prompt to keep product import language in skills only 2026-09-02 15:06:44 +02:00
.env.example Accept chat-uploaded files via ingest_uploaded_file middleware 2026-08-30 19:52:20 +02:00
.gitignore Accept chat-uploaded files via ingest_uploaded_file middleware 2026-08-30 19:52:20 +02:00
.python-version Distru agentic harness prototype: product CSV/XLSX import via Deep Agents + LangGraph Studio 2026-08-30 19:32:39 +02:00
chat_app.py system prompt to keep product import language in skills only 2026-09-02 15:06:44 +02:00
langgraph.json Distru agentic harness prototype: product CSV/XLSX import via Deep Agents + LangGraph Studio 2026-08-30 19:32:39 +02:00
notes.md Gate map/upload tools behind product-import skill load in subagent 2026-08-31 16:13:52 +02:00
pyproject.toml Distru agentic harness prototype: product CSV/XLSX import via Deep Agents + LangGraph Studio 2026-08-30 19:32:39 +02:00
README.md system prompt to keep product import language in skills only 2026-09-02 15:06:44 +02:00
run_stub.py Remove custom checkpointer for LangGraph dev; make stub port configurable via .env 2026-08-30 19:44:11 +02:00
uv.lock Distru agentic harness prototype: product CSV/XLSX import via Deep Agents + LangGraph Studio 2026-08-30 19:32:39 +02:00

Distru Agentic Harness — Prototype

A chat-testable prototype of the agentic harness described in ../Tech Spec.md, built on Deep Agents (top layer) on LangGraph. The first vertical slice is product CSV/XLSX import.

It demonstrates the whole harness shape from the spec:

  • Core agent with a durable Distru domain system prompt (src/distru_agent/agent.py)
  • Skill layer (skills/product-import/) loaded on demand
  • Subagent delegation via the task tool (generic subagent, auto-provisioned by deep-agents)
  • Context tool: customer_attributes
  • Action tools: map_products_file (dry-run, never uploads) and upload_products (the only real, gated action)
  • Human-in-the-loop: upload_products requires explicit consent
  • State: the LangGraph platform checkpointer provides conversation + interrupt persistence automatically (no custom one set)

Because there is no real Distru account, a small stub Distru server (src/distru_agent/server/stub_distru.py) faithfully emulates the internal contract: GET /customer/{id}/attributes and POST /upload-products (chunking, validation, partial upload, row-mapped error CSV). The agent's tools call it over HTTP.

Requirements

  • uv (Python env manager). It provisions a managed Python 3.12 — system Python being older is fine.

Setup

cd distru-prototype
uv sync                                   # create .venv and install deps
cp .env.example .env                      # then edit .env and paste your OpenAI key

In .env set:

OPENAI_API_KEY=sk-...
# optional
# OPENAI_MODEL=gpt-5.4-nano

Dependencies are pinned in pyproject.toml. The stub server needs python-multipart (already included) for uploads.

Run (two processes)

Because the tools call the stub over HTTP, start the stub first, then the chat app.

1) Start the stub Distru server

uv run python run_stub.py      # serves http://127.0.0.1:8000

Optional: DISTRU_STUB_URL and DISTRU_CUSTOMER_ID env vars override the URL and target customer (defaults http://127.0.0.1:8000 and 1001).

2) Start the chat app

uv run python chat_app.py      # serves http://127.0.0.1:8464

Open http://127.0.0.1:8464 in your browser. This is a minimal FastAPI chat page that accepts CSV/XLSX uploads (unlike LangGraph Studio's file picker, which only accepts images and PDFs). It runs the same agent graph locally with a thread + HITL consent gate.

Optional: DISTRU_CHAT_HOST and DISTRU_CHAT_PORT env vars override the bind address (default 127.0.0.1:8464).

How to test the import

In the chat page, attach a CSV/XLSX or reference a sample file path and send:

I have a product catalog file at ./sample_data/products_messy.csv
Please import it as new products.

The flow: the agent delegates to a subagent via the task tool. The subagent loads the customer's attributes, maps the file (dry-run), surfaces new vs existing entities, and — because upload_products is interrupt gated — pauses for your approval before any real upload. Inspect the map report (8 products, 1 duplicate auto-dropped, new strains + a new Pre-Rolls category, 2 fixable issues: a Metrc-disallowed : in one name and an unresolvable oz unit) and approve/reject.

The sample file deliberately includes edge cases from Tech Spec.md §4: exact duplicate, Metrc-disallowed name, a new category, new strains, an unresolvable unit, differing vendors.

Uploading a file in chat

Click Attach file in the chat page, select a CSV/XLSX, and send a message. The file is saved to uploads/ and the agent proceeds with the normal import flow.

Notes on the harness mapping (spec -> framework)

Tech Spec This prototype
Core agent + lean system prompt create_deep_agent(system_prompt=...)
Skill layer skills=["./skills/"] (SKILL.md, on-demand load)
Subagent delegation Generic subagent via task tool (auto-provisioned)
customer_attributes (context) tools/customer_attributes.py
map_products_file (dry-run) tools/map_products_file.py
upload_products (gated action) tools/upload_products.py
User consent / ask-the-user Deep Agents interrupt_on={"upload_products": ...} (HITL)
State (conversation / exec / artifacts) LangGraph platform checkpointer + temp files by handle

Extending

  • Add a capability = add a tool + (optionally) a skill, registered in agent.py.
  • The subagent + skill could later be invoked headless by a workflow trigger (cron / sheet scan) in a real LangGraph deployment.

Layout

distru-prototype/
├── langgraph.json                  # Studio/dev entry (graph = src/distru_agent/agent.py:graph)
├── pyproject.toml / .python-version
├── .env.example
├── run_stub.py                     # start the stub Distru server on :8000
├── chat_app.py                     # chat UI (FastAPI, port 8464) — primary test surface
├── sample_data/
│   └── products_messy.csv          # deliberately messy catalog to import
├── skills/
│   └── product-import/SKILL.md     # domain knowledge + consent policy
└── src/distru_agent/
    ├── agent.py                    # create_deep_agent -> graph
    ├── config.py                   # stub URL + customer id
    ├── distru_models.py            # Pydantic domain models
    ├── middleware.py               # saves chat-uploaded files, injects path
    ├── tools/
    │   ├── customer_attributes.py
    │   ├── map_products_file.py
    │   └── upload_products.py
    └── server/
        └── stub_distru.py          # FastAPI stub backend