Skip to main content

ConversationAdvisor (gnosis)

A CLI for ingesting and analyzing AI agent conversation logs (Claude Code, Codex) into a local SQLite store. Tracks every conversation through a three-stage lifecycle — discovered → imported → analyzed — so the expensive LLM steps (intent classification, pain-point detection) run once and their results are persisted for later inspection.

Features

  • Stateful ingestiongnosis discover walks the default Claude (~/.claude/projects/) and Codex (~/.codex/sessions/) roots, parses every session file, and stores conversations + messages into a SQLite DB. Incremental: re-running only appends new turns.
  • Intent classificationgnosis conversation classify segments a conversation into discrete tasks by classifying each user message along two axes (Type + Lifecycle). New-Task lifecycle entries become task rows.
  • Pain-point analysisgnosis task analyze runs a LangGraph DAG (scanner → classifier → resolver) against each task and persists problems with severity, suggested fix, and message-range evidence.
  • Friction-derived effectiveness — the headline mean_effectiveness metric (and the THS effectiveness weight, shown as the Friction bar) is computed by hla.models.friction_score: the deterministic per-tool effectiveness mean × a confirmed-friction-episode penalty. The deterministic tool scorer is an input to friction, not a separate metric. Per-message effectiveness (the tool component) is still stamped on Tool messages. See docs/effectiveness-metric.md and the friction-analysis section of CLAUDE.md.
  • Idempotent re-runs — every stage skips work that's already cached in the store; --rebuild flags force re-computation when needed.
  • Two output formats — most read-only commands accept --format table|json. JSON output is unconstrained (no truncation) for machine consumption.
  • Two providers — Anthropic (claude-haiku) or Ollama (qwen2.5:3b) for local inference.

Requirements

Installation

The CLI is distributed on PyPI as gnosis-air (the name gnosis on PyPI belongs to an unrelated package — installing it will NOT give you this tool). For a global CLI, install it with an isolated tool installer so the gnosis executable lands on your PATH:

uv tool install gnosis-air   # or: pipx install gnosis-air
gnosis --help

Plain pip install gnosis-air works too, but pip installs into the current environment: the gnosis executable goes to that environment's bin/ (e.g. .venv/bin/gnosis or the user-site scripts dir), which is only on PATH when that environment is active. If gnosis is "missing" after a pip install, that's why — prefer uv tool/pipx for a machine-wide CLI.

One hard requirement on the interpreter: gnosis's store loads the sqlite-vec extension, so the Python running it must be built with loadable SQLite extension support. uv-managed and python.org builds have it; pyenv-compiled Pythons usually don't (unless built with PYTHON_CONFIGURE_OPTS="--enable-loadable-sqlite-extensions") and fail at first DB access with AttributeError: 'sqlite3.Connection' object has no attribute 'enable_load_extension' — another reason uv tool install is the recommended path.

Structure triage for gnosis labs review-current-branch (mechanical formatting/comment-only files detected without LLM analysis) is built in: the tree-sitter wrapper is vendored and the grammar wheels are ordinary PyPI dependencies. No extras or extra package indexes are needed for any feature.

Development install (repo checkout)

uv sync                     # reads uv.lock
uv sync --extra dev         # adds pytest, black, mypy, ruff, torch
uv sync --extra lint        # just black/ruff/mypy (matches the CI lane)

pip install -e . works too — same [project] section in pyproject.toml.

For the Anthropic provider, drop your key in .env at the repo root or pass it via --key:

echo 'ANTHROPIC_API_KEY=sk-ant-…' > .env

For Ollama:

ollama pull qwen2.5:3b
ollama serve

The SQLite store lives at ~/.local/share/gnosis/gnosis.sqlite3 by default. Override per-invocation via --db-path (on discover/stats) or via the GNOSIS_DB_PATH env var (every command).

Embedding in another application

If you're calling gnosis as a subprocess from another application (an IDE plugin, a desktop app, a CI step), the parent doesn't need to assume uv, pip, or even Python is set up the way the user's interactive shell expects. Use scripts/bootstrap.py to install uv (if missing) and then install gnosis into an isolated uv tool venv. Cross-platform — works on Linux, macOS, and Windows.

import subprocess, sys

# One-time bootstrap. Idempotent — safe to call on every launch.
result = subprocess.run(
    [sys.executable, "scripts/bootstrap.py", "--gnosis-ref", "v0.3.0"],
    capture_output=True, text=True, check=True,
)
gnosis_binary = result.stdout.strip().splitlines()[-1]

# Every subsequent invocation: subprocess the binary directly.
subprocess.run(
    [gnosis_binary, "conversation", "messages", "3", "--task", "22", "--format", "json"],
    env={**os.environ, "GNOSIS_DB_PATH": "/path/parent/controls",
         "ANTHROPIC_API_KEY": "sk-ant-…"},
    capture_output=True, check=True,
)

The bootstrap script:

  • Looks for uv on PATH and at astral's standard install locations (~/.local/bin/uv, etc.).
  • If missing, runs astral's official installer (curl … | sh on Unix; irm … | iex on Windows) — no third-party hosts.
  • Runs uv tool install --python 3.12 git+<gnosis-url>@<ref> to create an isolated venv.
  • Verifies the install by importing gnosis's LLM entry chain in the tool venv, so an incomplete env (e.g. a compiled transitive dep dropped by a partial/proxy-blocked download) fails at bootstrap time with a clear message rather than as a mystery ModuleNotFoundError at runtime.
  • Mirrors everything — uv's output, exit code, resolved path, verification result — to a timestamped log at <tempdir>/gnosis/gnosis-bootstrap__<ts>.log, so a failed install stays inspectable after the subprocess exits.
  • Prints the resolved gnosis binary path to stdout's last line; progress goes to stderr.

Pass --reinstall (alias --force) to force a full rebuild of gnosis and all its dependencies — the repair path when a user's env is left incomplete (uv tool install is otherwise a no-op if gnosis is already present):

subprocess.run([sys.executable, "scripts/bootstrap.py", "--gnosis-ref", "v0.3.0", "--reinstall"], check=True)

The parent should always invoke gnosis with --format json (machine-parseable output) and pass --key / --db-path (or the ANTHROPIC_API_KEY / GNOSIS_DB_PATH env vars) so the embedded gnosis never touches the user's .env or default state path. See python scripts/bootstrap.py --help for the available flags.

CLI Reference

Every command supports -h / --help. The CLI is a Click group; sub-groups (conversation, task) have their own subcommands.

Lifecycle in 30 seconds

gnosis discover                  # 1. ingest all local sessions into the store
gnosis conversation classify 7          # 2. classify intents + extract tasks for conversation #7
gnosis task analyze 7                 # 3. run pain-point analysis on every task in #7
gnosis task show 7              # 4. browse the results

Each step is incremental and persists its output, so step 4 doesn't re-run the LLM. The conversation's status field (visible in gnosis conversation list) reflects how far it's progressed:

Discovered  →  Imported  →  Analyzed
   (1)          (2)           (3)

discover — ingest trajectories into the store

gnosis discover [--extra-folder PATH]... [--db-path PATH]
                [--rebuild | --rebuild-conversation PATH...]
                [--benchmark] [-v]

Scans ~/.claude/projects/ and ~/.codex/sessions/ (plus any --extra-folder paths), parses every session, and upserts conversations + messages into the store. Idempotent — re-running only appends new turns.

Flag Purpose
--extra-folder PATH Additional folder to scan recursively for *.jsonl. Repeatable.
--db-path PATH Override the default DB location. Beats $GNOSIS_DB_PATH.
--rebuild Drop every conversation row first, then re-import. FK cascade clears messages, intents, tasks, problems too.
--rebuild-conversation PATH Scoped rebuild — drop one conversation's row + cascade, then re-ingest it. Repeatable. Mutually exclusive with --rebuild.
--benchmark Print a stage time / tokens / cost report at the end. (Discover doesn't call any LLMs, so tokens are zero — useful for seeing how long the discovery walk vs ingest pass took.)
-v / --verbose Per-call token usage, parser skips, progress bars.
gnosis discover                                       # default roots only
gnosis discover --extra-folder ~/old-sessions/        # plus an archive folder
gnosis discover --rebuild                             # wipe + re-import everything
gnosis discover --rebuild-conversation ~/.claude/projects/.../foo.jsonl
gnosis discover --benchmark                           # include the per-stage report

conversation list — browse the store

gnosis conversation list [--format table|json]

Compact table sorted by stable # identifier. Columns: #, Provider, Title, Path, Project, Status. Read-only — no LLM.

gnosis conversation list
gnosis conversation list --format json

conversation show — full record for one conversation

gnosis conversation show <id-or-path> [--format table|json]

Identifier is the # from list (integer) or the absolute path on disk. Prints every stored field plus the tasks summary.

gnosis conversation show 7
gnosis conversation show /Users/me/.claude/projects/.../uuid.jsonl
gnosis conversation show 7 --format json

conversation classify — segment one or more conversations into tasks

gnosis conversation classify [IDENTIFIER]
                              [--project NAME] [--folder PATH] [--classify-all]
                              [--benchmark]
                              [-p ollama|anthropic] [--key KEY] [-v]

Runs the two-axis intent classifier on every human message in each matched conversation, in chunks of ≤ 4 humans per LLM call. Incremental: humans with a stored intent are skipped. New-Task lifecycle classifications become task rows; the conversation flips to Imported.

Exactly one selector must be set:

Selector Effect
<IDENTIFIER> One conversation, by # (digits) or absolute path.
--project NAME Every conversation whose project column equals NAME.
--folder PATH Every conversation whose path starts with PATH/.
--classify-all Every conversation in the store.

Conditional re-classification wipe. If at least one new intent is saved this run, prior analysis state is invalidated: tasks rows replaced (their analyzed_at/token columns dropped), all problems deleted, last_analysis cleared. On a true no-op re-run (every human already classified), nothing changes — analysis state is preserved.

--benchmark prints a per-conversation time / tokens / cost report at the end.

gnosis conversation classify 7                                       # single by #
gnosis conversation classify /Users/me/.claude/.../uuid.jsonl        # single by path
gnosis conversation classify --project /Users/me/projects/foo        # by project
gnosis conversation classify --folder ~/.claude/projects/            # by folder prefix
gnosis conversation classify --classify-all                          # everything
gnosis conversation classify --classify-all --benchmark

conversation friction — multi-tier inefficiency analysis

gnosis conversation friction <id-or-path>
                              [--rebuild] [--benchmark] [--format table|json]
                              [-p ollama|anthropic] [--key KEY] [-v]

Finds the friction a per-tool-call scan misses — defects the user had to paste back, dissatisfaction loops, blocked stalls, wrong-context requests, output-visibility misses. A deterministic pass (no LLM) surfaces candidate episodes; a single batched LLM call then adjudicates each (real? root cause? wasted turns? one-line remedy). ~$0.02 per conversation on Haiku.

Results persist to the store. Re-running renders the cached verdicts without an LLM call; pass --rebuild to re-detect and re-adjudicate. --format json emits the full payload (episodes + verdicts + a wasted-turn rollup).

gnosis conversation friction 2                 # detect + adjudicate (or render cached)
gnosis conversation friction 2 --benchmark     # + time / tokens / cost
gnosis conversation friction 2 --format json   # structured payload
gnosis conversation friction 2 --rebuild       # force re-analysis

task list — browse one conversation's tasks

gnosis task list <id-or-path> [--format table|json]

Compact table: #, Status (not analyzed / analyzed), Type, Position, Title. Read-only.

gnosis task list 7
gnosis task list 7 --format json

task show — detailed task view

gnosis task show <id-or-path> [--task N] [--format table|json]

Without --task: every task is printed in full detail. With --task N: just that one (errors if out of range).

Each task includes: intent segments (text + Type + Lifecycle), message-range start → end, start_time / end_time from the source JSONL, per-type step counts (user, agent, tool), analyzed_at and tokens spent (when analyzed), and any persisted problems with their severity / fix / analysis. Read-only.

gnosis task show 7
gnosis task show 7 --task 2
gnosis task show 7 --format json

task analyze — find pain points in a conversation

gnosis task analyze <id-or-path> [--task N] [--rebuild] [--benchmark]
                    [-p ollama|anthropic] [--key KEY] [-v]

Runs the LangGraph pain-point pipeline against each task in a conversation. Per-task idempotent — tasks already analyzed (analyzed_at stamped) are rendered from the store without re-running the LLM. With --task N it scopes to one task.

--rebuild clears problems + analyzed_at for the scope (whole conversation or single task) up front, then re-analyzes everything. Use after fixing prompt/scoring code or when you want a fresh read.

--benchmark prints a per-task time / tokens / cost report at the end.

On success: problems persisted, tasks.analyzed_at / input_tokens / output_tokens stamped per task, conversations.last_analysis updated → status flips to Analyzed.

gnosis task analyze 7                                   # analyze unanalyzed tasks in #7
gnosis task analyze 7 --task 2                          # only task #2 (cached if already done)
gnosis task analyze 7 --rebuild                         # force re-analysis of every task
gnosis task analyze 7 --task 2 --rebuild                # force re-analysis of just task #2
gnosis task analyze 7 --benchmark                       # add the per-stage report

stats — store summary

gnosis stats [--db-path PATH]

Prints: total conversations, distinct projects, median human/agent/tool messages per conversation.

gnosis stats
GNOSIS_DB_PATH=/tmp/scratch.sqlite3 gnosis stats

score and intents (legacy, file-based)

These two haven't been migrated to the store. They take a session file path (or directory) directly.

gnosis score   <session.jsonl> [-p anthropic|ollama] [--key KEY] [-v]
gnosis intents <session.jsonl> [-p anthropic|ollama] [--key KEY] [-v]

score returns one of Bad / Ok / Good for the whole conversation. intents prints the full per-message intent table.


Architecture

High-level data flow

The pipeline is session file → parsed messages → SQLite store → on-demand LLM stages. The store is the single source of truth for everything except the freshly-parsed BaseMessage list during discover.

flowchart TB
    classDef store fill:#dde,stroke:#666
    classDef agent fill:#fdd,stroke:#666

    subgraph Files["~/.claude/projects/, ~/.codex/sessions/, --extra-folder"]
        SF[".jsonl files"]
    end

    SF --> Disc

    subgraph Layers["src/"]
        direction TB
        Disc["discovery/<br/>find trajectory files<br/>per provider"]
        Pars["parsers/<br/>JSONL → list[BaseMessage]<br/>(Claude / Codex)"]
        HLA["hla/<br/>conversation_outline<br/>(skeleton, chunks, IDE-strip)"]
        Agents["agents/<br/>intent_analyzer<br/>conversation_optimizer<br/>user_satisfaction"]
        Comp["analyzers/<br/>comprehensive_scan/<br/>(LangGraph DAG)"]
        Cmds["commands/<br/>one file per CLI verb"]
        Store[("storage/<br/>SQLite store<br/>~/.local/share/gnosis/<br/>gnosis.sqlite3")]
    end

    Disc --> Pars
    Pars --> HLA
    HLA --> Agents
    Agents --> Comp
    Cmds <--> Store
    Cmds --> Agents
    Pars --> Store
    Store:::store
    Agents:::agent
    Comp:::agent

    Store -. read .-> CLI["gnosis CLI<br/>(Click)"]
    CLI --> Cmds

Layer rules (load-bearing — see CLAUDE.md for the full list):

  • commands/ is the only layer the CLI talks to.
  • agents/ is where every LLM call lives. Never imports from parsers/ or discovery/ — agents work on already-parsed BaseMessage lists.
  • hla/ (no-LLM data shaping) and agents/ (LLM) are siblings.
  • storage/ is provider-agnostic; everything in commands/, agents/, and hla/ reads/writes through its helpers.

SQLite schema

Five tables, all in SQLite STRICT mode. The lifecycle is encoded in the FK cascades.

erDiagram
    conversations ||--o{ messages : "cascades on delete"
    messages ||--o| intents : "(path, position) cascades"
    intents ||--o| tasks : "(path, position) cascades"
    conversations ||--o{ problems : "cascades on delete"

    conversations {
        INTEGER id "stable # identifier"
        TEXT path PK "absolute trajectory path"
        TEXT provider "claude | codex"
        TEXT title
        TEXT project
        TEXT created_at
        TEXT last_updated_at
        TEXT first_ingested_at
        TEXT last_ingested_at
        TEXT last_task_analysis "stamped by conversations classify"
        TEXT last_analysis "stamped by analyze"
    }
    messages {
        TEXT conversation_path PK,FK
        INTEGER position PK "0-based parser ordinal"
        TEXT type "human | agent | tool"
        TEXT uuid "stable for claude"
        TEXT timestamp "from raw JSONL entry"
        TEXT payload "pydantic model_dump_json"
    }
    intents {
        TEXT conversation_path PK,FK
        INTEGER position PK,FK "→ messages.position"
        TEXT title
        TEXT segments_json "JSON list[{text, type, lifecycle}]"
        TEXT classified_at
    }
    tasks {
        TEXT conversation_path PK,FK
        INTEGER number PK "1-indexed per conversation"
        INTEGER position UK,FK "→ intents.position"
        TEXT type
        TEXT title
        TEXT analyzed_at "NULL = classified, NOT NULL = analyzed"
        INTEGER input_tokens
        INTEGER output_tokens
    }
    problems {
        TEXT conversation_path FK
        INTEGER task_number "FK by convention; NULL for fallback runs"
        TEXT type
        INTEGER relevant_message_range_start
        INTEGER relevant_message_range_end
        REAL severity
        TEXT description
        TEXT suggested_fix
        TEXT analysis
        TEXT created_at
    }

The cascade chain: deleting a conversation row → cascades to its messages and problems rows → which cascades to intents → which cascades to tasks. This is what makes discover --rebuild a clean wipe and what keeps --rebuild-conversation <path> precisely scoped.

Per-conversation status derivation

Computed in storage/conversations.py:compute_status from two signals:

stateDiagram-v2
    [*] --> Discovered : gnosis discover
    Discovered --> Imported : gnosis conversation classify
    Imported --> Analyzed : gnosis task analyze
    Analyzed --> Imported : conversation classify (invalidates analysis)
    Imported --> Imported : conversation classify (incremental)
    Analyzed --> Analyzed : analyze (cached re-render)
    Analyzed --> Analyzed : analyze --rebuild
  • Discoveredtasks table is empty for this conversation.
  • Imported — at least one task row, last_analysis IS NULL.
  • Analyzedlast_analysis IS NOT NULL.

Per-task status is independent and shown in task list / task show:

  • not analyzed — task row exists, analyzed_at IS NULL.
  • analyzedanalyzed_at IS NOT NULL (problems persisted, tokens recorded).

discover flow

flowchart TD
    cli["gnosis discover [--extra-folder PATH]... [--rebuild | --rebuild-conversation PATH]..."]
    cli --> resolveDB["resolve DB path<br/>(--db-path > $GNOSIS_DB_PATH > default)"]
    resolveDB --> openDB["open + migrate schema<br/>(CREATE TABLE IF NOT EXISTS + idempotent ALTER TABLE)"]
    openDB --> rebuild{--rebuild?}
    rebuild -- yes --> wipeAll["DELETE FROM conversations<br/>(cascades to all tables)"]
    rebuild -- no --> rebuildOne{--rebuild-conversation?}
    rebuildOne -- yes --> wipeOne["DELETE FROM conversations<br/>WHERE path IN (...) (cascades)"]
    rebuildOne -- no --> walk
    wipeAll --> walk
    wipeOne --> walk
    walk["discover_trajectories(project=None)<br/>+ glob each --extra-folder"]
    walk --> perTraj[for each trajectory]
    perTraj --> parse["read + parsers.get_messages(content)<br/>list[BaseMessage] with timestamps"]
    parse --> fill["hla.fill_tool_success(msgs[existing_count:])<br/>(sentence-transformer; cached centroid)"]
    fill --> upsert["upsert conversation + append messages<br/>(BEGIN IMMEDIATE; idempotent)"]
    upsert --> uuidCheck{first N stored uuids<br/>match parsed prefix?<br/>(claude only)}
    uuidCheck -- yes --> next[next trajectory]
    uuidCheck -- no --> skip["skip with warning<br/>(tree branch divergence)"]
    skip --> next
    next --> done(["summary line:<br/>N scanned, M new conversations,<br/>K new messages"])

conversation classify flow

flowchart TD
    cli["gnosis conversation classify (one selector required)"]
    cli --> resolve["find_conversation_by_identifier<br/>(digits → id, else path)"]
    resolve --> miss{found?}
    miss -- no --> err1["Error → stderr"]
    miss -- yes --> load["load_messages + load_intents"]
    load --> diff["new_positions = {all humans} − {classified}"]
    diff --> any{new_positions empty?}
    any -- yes --> stamp
    any -- no --> chunk["create_messages_skeleton_outline<br/>chunk_messages(max_humans=4)"]
    chunk --> filter["keep chunks containing ≥1 new human"]
    filter --> llm["per chunk:<br/>analyze_intents_from_outline<br/>(retry once on missing indexes)"]
    llm --> persist["save_intents (INSERT OR IGNORE)<br/>only new positions"]
    persist --> stamp
    stamp["compute task boundaries<br/>save_tasks (DELETE + INSERT)<br/>set_last_task_analysis(now)<br/>delete_problems(path)<br/>UPDATE last_analysis = NULL"]
    stamp --> summary["summary:<br/>N tasks identified<br/>+ note if prior analysis was cleared"]

task analyze flow

flowchart TD
    cli["gnosis task analyze &lt;id-or-path&gt; [--task N] [--rebuild] [--benchmark]"]
    cli --> guard["check: conversation exists,<br/>last_task_analysis IS NOT NULL"]
    guard --> scope{--task N?}
    scope -- yes --> single["scope = [task N]"]
    scope -- no --> all["scope = every task"]
    single --> rebuildQ
    all --> rebuildQ
    rebuildQ{--rebuild?}
    rebuildQ -- yes --> wipe["upfront wipe in scope:<br/>delete_problems(...) +<br/>UPDATE tasks SET analyzed_at=NULL"]
    rebuildQ -- no --> part["partition scope<br/>by analyzed_at IS NULL/NOT NULL"]
    wipe --> part2["to_analyze = scope<br/>cached = []"]
    part --> loop
    part2 --> loop
    loop[for each task in to_analyze]
    loop --> benchmark["with benchmark(task #N) as section:<br/>find_problems(msgs[start:end])<br/>(comprehensive_scan LangGraph:<br/>Scanner → Classifier×N → Resolver)"]
    benchmark --> save["BEGIN IMMEDIATE:<br/>delete_problems(path, task=N)<br/>save_problems(...)<br/>mark_task_analyzed(N, tokens, now)"]
    save --> loop
    loop -- done --> render["load_problems(path)<br/>reconstruct full TaskAnalysis from cached + new<br/>+ stored task ranges<br/>compute IQC / per-task TQC<br/>print report + benchmark"]

find_problems invokes the LangGraph DAG in analyzers/comprehensive_scan/ — a pre-scanner node first stamps anchor-driven effectiveness scores on each Tool / Agent message (see docs/effectiveness-metric.md); a cost-scanner stamps cost_effectiveness on each Human; then scanner finds suspect ranges, classifier verifies each via a sub-agent with get_messages_by_range / get_detailed_message tools, and resolver assigns severity + fix. See CLAUDE.md for the full walkthrough.

Project structure

.
├── src/
│   ├── gnosis.py                          # Click CLI entry point
│   ├── main.py                            # legacy argparse entry (discover/show)
│   ├── llm.py                             # provider abstraction + llm_invoke
│   ├── utils.py
│   ├── tool_output_classifier.py          # sentence-transformer for tool success/failure
│   ├── pattern_scanner.py                 # heuristic helpers (unused; pending cleanup)
│   ├── commands/                          # one file per CLI verb
│   │   ├── analyze.py, conversations.py,
│   │   │ discover.py, intents.py, score.py,
│   │   │ show.py, stats.py, tasks.py
│   │   └── runner.py                      # iter_session_files + run_over_path
│   ├── parsers/                           # JSONL → list[BaseMessage]
│   │   ├── base.py, claude.py, codex.py, messages.py
│   ├── discovery/                         # find session files per provider
│   ├── storage/                           # SQLite store
│   │   ├── db.py                          # connection, schema bootstrap, migrations
│   │   └── conversations.py               # CRUD + StoredTask/StoredIntent/StoredProblem
│   ├── hla/                               # non-LLM data shaping
│   │   ├── conversation_outline.py        # outline, skeleton, chunking, ide-block strip
│   │   ├── benchmark.py                   # BenchmarkSection + UsageTracker
│   │   └── models.py                      # Problem, TaskAnalysis, ByTaskAnalysisResult
│   ├── agents/                            # LLM-driven analyzers
│   │   ├── intent_analyzer.py             # two-axis classification
│   │   ├── conversation_optimizer.py      # find_problems (entry to comprehensive_scan)
│   │   └── user_satisfaction.py
│   ├── analyzers/comprehensive_scan/      # LangGraph DAG: scanner → classifier → resolver
│   ├── analysis_tools/                    # LangChain tools used by sub-agents
│   └── contracts/                         # shared types (IntentType, TaskBoundary, etc.)
├── data/sessions/                         # bundled sample conversations
├── experiments/                           # exploratory notebooks (not tests)
└── .github/workflows/lint.yml             # black + ruff + mypy on every PR

LLM providers

LLMConfig in src/llm.py carries provider, model, and per-million-token pricing. Two presets:

Preset Provider Model
LLMConfig.claude_haiku_4_5_20251001() Anthropic claude-haiku-4-5-20251001
LLMConfig.ollama_qwen_2_5__3b() Ollama qwen2.5:3b

llm_invoke() is the single entry point — it builds the langchain client, runs structured-output inference, wires the UsageTracker callback so per-task tokens flow into the active BenchmarkSection, and prints per-call cost.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gnosis_air-0.0.9.tar.gz (940.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gnosis_air-0.0.9-py3-none-any.whl (922.8 kB view details)

Uploaded Python 3

File details

Details for the file gnosis_air-0.0.9.tar.gz.

File metadata

  • Download URL: gnosis_air-0.0.9.tar.gz
  • Upload date:
  • Size: 940.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for gnosis_air-0.0.9.tar.gz
Algorithm Hash digest
SHA256 6c966d97347aa9e50f1f1452a45932f9ea95368c30822e9115558f93194b6346
MD5 079e7d53eadaaf7d9ed41ec18504aa99
BLAKE2b-256 ef6a0c8574b22226e842a0994322d94cf4086efc4885062b57686be8ff5a45cb

See more details on using hashes here.

Provenance

The following attestation bundles were made for gnosis_air-0.0.9.tar.gz:

Publisher: publish-pypi.yml on JetBrains/air-trajectory-analysis

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gnosis_air-0.0.9-py3-none-any.whl.

File metadata

  • Download URL: gnosis_air-0.0.9-py3-none-any.whl
  • Upload date:
  • Size: 922.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for gnosis_air-0.0.9-py3-none-any.whl
Algorithm Hash digest
SHA256 afe6bfba2ba0cbc996e75c5b470d85f6ce5644df79a3c4f8e03bc18432ded852
MD5 7b85d00c0c855664419b2a3ce503f607
BLAKE2b-256 02f268c3fbdd2de1923369b1762226010c5003b8374bc1b51790abf3e96fff6b

See more details on using hashes here.

Provenance

The following attestation bundles were made for gnosis_air-0.0.9-py3-none-any.whl:

Publisher: publish-pypi.yml on JetBrains/air-trajectory-analysis

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.0.10

2 files

This release

0.0.9 This release

2 files

0.0.8

2 files

0.0.7

2 files

0.0.6

2 files

0.0.5

2 files

0.0.3

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page