Skip to content
Open
Changes from 1 commit
Commits
Show all changes
408 commits
Select commit Hold shift + click to select a range
b120840
feat(skills): revamp using-ccproxy-api with install guide, update tem…
starbaser Apr 12, 2026
115d100
fix(cli): install command no longer rejects existing config directories
starbaser Apr 12, 2026
978f17f
refactor(ccproxy): add mode field to TransformMeta for routing logic
starbaser Apr 12, 2026
c076a70
chore: release audit — strip AI slop, fix blockers, clean config
starbaser Apr 12, 2026
376c204
fix(namespace): enable localhost→host connectivity inside WireGuard jail
starbaser Apr 12, 2026
8419c5f
docs(CLAUDE.md): add namespace networking, flow inspection, Gemini ro…
starbaser Apr 12, 2026
e43d838
test: fix stale assertions after install and TransformMeta refactors
starbaser Apr 12, 2026
c454846
feat(lightllm): add Gemini/Vertex AI context caching via cachedConten…
starbaser Apr 12, 2026
8d09bdd
test: comprehensive coverage recovery from 76% to 90%
starbaser Apr 12, 2026
eaccaf4
fix(lightllm): adapt to litellm API changes in sign_request and trans…
starbaser Apr 12, 2026
824983c
refactor: resolve TODO audit — immediate fixes and deferred items D1-…
starbaser Apr 12, 2026
afbc2ae
refactor(compliance): extract ComplianceMerger class for user-extensi…
starbaser Apr 13, 2026
6860d12
feat(scripts): add verify_cch script with compute_cch function
starbaser Apr 13, 2026
6a5d9a2
feat(flows): add relative timestamp column to flows list output
starbaser Apr 13, 2026
c2becf6
ci: add notify-marketplace workflow
starbaser Apr 13, 2026
556ec21
docs: document defaultSettings export and port conventions
starbaser Apr 13, 2026
ea6fad8
feat(ccproxy): add verify_outbound_reachability startup probe
starbaser Apr 13, 2026
fc14bfb
refactor(ccproxy): zero-default provider timeout (Portkey parity)
starbaser Apr 13, 2026
f8f6e31
fix(ccproxy): restore stdout purity for `ccproxy run`
starbaser Apr 13, 2026
5d1ae7c
fix(nix): suppress uv SSL_CERT_FILE warning in claude-ccproxy wheel b…
starbaser Apr 13, 2026
1c43e6a
fix(deps): lift litellm supply chain pin to >=1.83.0
starbaser Apr 13, 2026
a4ed008
refactor: replace contentview methods with __call__ and _render
starbaser Apr 14, 2026
f5fd621
feat(ccproxy): add gemini_oauth_refresh hook for token recovery
starbaser Apr 14, 2026
a726064
refactor(ccproxy): extract HAR builder functions from flows
starbaser Apr 14, 2026
e90f143
refactor(ccproxy)!: migrate ClientRequestContentview to Contentview API
starbaser Apr 14, 2026
b9c7977
fix(nix): inject setuptools into pyperclip source build
starbaser Apr 14, 2026
5884664
docs(ccproxy): document HAR output in flows --help and CLAUDE.md
starbaser Apr 14, 2026
1d50a53
fix(ccproxy): triage log noise and resurrect handle_transform_response
starbaser Apr 14, 2026
2d5c2c9
refactor(ccproxy): replace attrs with pydantic for CLI command classes
starbaser Apr 14, 2026
575cb94
fix(ccproxy): skip Authorization header when web_password is unset
starbaser Apr 14, 2026
c7227d9
fix(ccproxy): apply compliance profile to transformed list-shaped system
starbaser Apr 14, 2026
47b5eee
chore(scripts): remove verify_cch.py script
starbaser Apr 14, 2026
047618c
refactor(ccproxy)!: replace debug flag with log_level + log_file, add…
starbaser Apr 14, 2026
ab41860
style: reorder kitstore.nix repositories alphabetically
starbaser Apr 14, 2026
51be8c7
refactor(ccproxy)!: rewrite flows CLI + delegate HAR to SaveHar
starbaser Apr 14, 2026
49ca806
refactor(ccproxy): group hooks by stage in show_status display
starbaser Apr 14, 2026
c67a9e6
refactor(ccproxy): extract load_hooks from inspector.pipeline to loader
starbaser Apr 14, 2026
94369bc
feat(ccproxy): add render_pipeline for rich DAG visualization
starbaser Apr 14, 2026
73820d1
refactor(ccproxy)!: remove to_mermaid and to_ascii from HookDAG
starbaser Apr 14, 2026
daddea6
feat: add pydantic and rich repository configurations
starbaser Apr 14, 2026
888a887
docs: remove dag-viz command references from all documentation
starbaser Apr 14, 2026
68b285f
refactor(ccproxy): accept comma-separated flow ids in ccproxy_dump
starbaser Apr 15, 2026
fb35840
refactor(ccproxy): extract _FlowsBase and add jq filtering to flows
starbaser Apr 15, 2026
7f5ad4a
docs: document flows jq filtering and compare subcommand
starbaser Apr 15, 2026
7588818
feat(ccproxy): add FlowsConfig for CLI jq filter defaults
starbaser Apr 15, 2026
5f7adfe
style(ccproxy): reformat console.print call and add FlowsCompare
starbaser Apr 15, 2026
21596f9
style(ccproxy): collapse multi-line ValueError in load_hooks
starbaser Apr 15, 2026
70c1534
style(ccproxy): remove trailing blank lines in dag and executor
starbaser Apr 15, 2026
d92802b
docs!: rename 'ccproxy install' to 'ccproxy init' across all docs
starbaser Apr 15, 2026
c86d2da
refactor(ccproxy)!: rename install command to init
starbaser Apr 15, 2026
ad6c579
feat(ccproxy): add XSRF token support to delete_flow
starbaser Apr 15, 2026
0f03fd6
refactor(ccproxy)!: move default config dir to XDG_CONFIG_HOME
starbaser Apr 16, 2026
e0046dd
docs: move USAGE.md from docs/ to project root
starbaser Apr 16, 2026
a72744a
feat(ccproxy): add reroute_gemini inbound hook for Gemini SDK traffic
starbaser Apr 16, 2026
ecad102
Merge remote-tracking branch 'origin/main' into dev
starbaser Apr 16, 2026
3afef0f
test: add test_oauth_user_agent and test_hooks for oauth/routing
starbaser Apr 16, 2026
292746e
feat(inspector): unify HttpSnapshot model and capture provider responses
starbaser Apr 16, 2026
e47e305
docs: update CLAUDE.md for HttpSnapshot model and provider response c…
starbaser Apr 16, 2026
51e15e7
refactor(flows): replace Rich decoration with git diff for diff/compa…
starbaser Apr 17, 2026
aec7465
fix(hooks): handle stale OAuth and metadata leak in reroute_gemini
starbaser Apr 17, 2026
0b14c35
docs: remove CONVENTIONS.md reference from CLAUDE.md
starbaser Apr 17, 2026
50d7439
feat(compliance): add profile_path for shared compliance profiles
starbaser Apr 17, 2026
91d86ca
feat(compliance): replace auto-observation with user-curated flow see…
starbaser Apr 18, 2026
d0d6587
fix(compliance): broaden apply_compliance guard to OAuth-injected flows
starbaser Apr 18, 2026
eaae270
refactor(compliance)!: rename ComplianceMerger to ComplianceStamper, …
starbaser Apr 18, 2026
99046f6
refactor(compliance)!: unify Envelope across seeding and stamping pip…
starbaser Apr 18, 2026
aab5b9b
refactor(ccproxy): replace stamper with husk-based compliance flow
starbaser Apr 19, 2026
ba38a4c
feat(ccproxy)!: add husk hook with prepare/fill compliance functions
starbaser Apr 19, 2026
620eec9
refactor(ccproxy): replace compliance stamping with apply_husk
starbaser Apr 19, 2026
33a07f5
refactor(ccproxy): remove extract_envelope and extractor module
starbaser Apr 19, 2026
1e71cd1
refactor(ccproxy): remove classifier.py and feature classification
starbaser Apr 19, 2026
5bd72c1
refactor(ccproxy)!: replace compliance stamper with husk pipeline
starbaser Apr 19, 2026
c723764
docs!: replace compliance profile system with seed/husk framework
starbaser Apr 19, 2026
c5c8ce9
refactor!: replace stamp_compliance hook with husk prepare/fill pipeline
starbaser Apr 19, 2026
6754e5c
refactor(ccproxy): gate HookDAG dependencies by priority order
starbaser Apr 19, 2026
257273d
style(ccproxy): add explicit str() casts in reroute_gemini
starbaser Apr 19, 2026
4668396
fix(ccproxy): husk preserves transport routing and auth headers
starbaser Apr 21, 2026
286415a
refactor(ccproxy): replace strip_system_blocks_except_first with para…
starbaser Apr 21, 2026
0895fd2
refactor!: rename compliance/seed/husk to shaping/shape
starbaser Apr 21, 2026
fe91847
fix(ccproxy): align _do_shape output with ccproxy.shape response format
starbaser Apr 21, 2026
c23fd6f
fix(ccproxy): cast FlowMeta keys to str in _strip_runtime_metadata
starbaser Apr 21, 2026
e41f6f5
fix(ccproxy): strip non-string metadata keys in shape capturer
starbaser Apr 21, 2026
07aa79b
fix(ccproxy): bind parenthesized args by keyword in shape hook entries
starbaser Apr 21, 2026
241f18e
refactor!: replace untyped dict pipeline with Pydantic AI typed objects
starbaser Apr 22, 2026
95d8733
docs: update CLAUDE.md for typed pipeline and Context.from_request
starbaser Apr 22, 2026
e953802
chore: remove stale artifacts and unused constants
starbaser Apr 22, 2026
2d71237
refactor: promote flows to top-level ccproxy.flows package
starbaser Apr 22, 2026
290dd89
refactor: apply CONVENTIONS.md coding standards across codebase
starbaser Apr 22, 2026
a59562d
refactor!: replace hardcoded shaping pipeline with config-driven per-…
starbaser Apr 23, 2026
5cab185
fix: skip shaping when incoming UA matches shape's UA family
starbaser Apr 23, 2026
bc6cafa
feat: add merge strategy slice parameter and replace callbacks with i…
starbaser Apr 23, 2026
0daf08b
fix: add context_management to content_fields
starbaser Apr 23, 2026
a64e5a1
docs: fix staleness across all docs, add usage.md
starbaser Apr 23, 2026
c725989
fix: revert shape_hooks to module-level paths
starbaser Apr 23, 2026
5ecf7e2
fix: remove system prepend_shape from default merge strategies
starbaser Apr 23, 2026
5021dab
feat: add commitbee_compat hook for raw JSON responses
starbaser Apr 23, 2026
c8ad426
fix: strengthen commitbee_compat JSON instruction, restore prepend_shape
starbaser Apr 23, 2026
226393e
fix: strip accept-encoding from shape headers
starbaser Apr 23, 2026
7a61628
feat: add DeepSeek V4 provider defaults
starbaser Apr 27, 2026
880aee5
docs: update flow CLI usage and hook references for 2.0.0
starbaser Apr 27, 2026
3afcba1
refactor: generate template YAML from nix/defaults.nix
starbaser Apr 28, 2026
10ab1b9
docs: document template generation workflow in CLAUDE.md
starbaser Apr 28, 2026
de186d5
fix: disable body streaming to prevent 502 on large reverse proxy req…
starbaser Apr 28, 2026
d6f52cf
feat: add Gemini shaping provider profile and content injection hook
starbaser Apr 28, 2026
024cee4
add streaming sync to inject_gemini_content shape hook
starbaser Apr 29, 2026
f4cd8d1
fix gemini shape_hooks to use module paths, not function paths
starbaser Apr 29, 2026
eeba314
strip shape systemInstruction and tools from gemini requests
starbaser Apr 29, 2026
b287f6b
revert hardcoded systemInstruction/tools stripping
starbaser Apr 29, 2026
a80df47
feat: add glom-based cache breakpoint shaping hooks
starbaser May 1, 2026
b9f2208
update CLAUDE.md with glom caching hooks documentation
starbaser May 1, 2026
621746e
docs: add cache breakpoint hooks to shaping and configuration guides
starbaser May 1, 2026
42196a4
refactor: standardize hook system on glom for body mutations
starbaser May 1, 2026
e13572a
docs: document glom as standard body mutation primitive across hook s…
starbaser May 1, 2026
228a79a
refactor: rename shaping callbacks to regenerate, fix shape_hooks type
starbaser May 1, 2026
a6fdfa3
feat: unify Gemini handling into single gemini_cli outbound hook
starbaser May 3, 2026
427494b
feat: gemini_capacity_fallback hook + restore conditional UA masquerade
starbaser May 3, 2026
8d963b7
feat: add specs/oauth/mcp packages, billing-header regen, model catalog
starbaser May 3, 2026
249e864
chore: drop inject_claude_code_identity hook (obviated by shape replay)
starbaser May 4, 2026
6f75ef8
chore: update flake.lock dependencies
starbaser May 4, 2026
b1d5af7
feat: recursive consumer-config merge, shape-DAG render, gemini strip…
starbaser May 4, 2026
1b92ff2
feat: add Portkey-style /health and / endpoints
starbaser May 4, 2026
c728a0b
feat: per-project file logging + opt-in journald identifier
starbaser May 4, 2026
3ae69fb
docs: regenerate CLAUDE.md, drop stale notes
starbaser May 4, 2026
1e95ab4
feat: extract billing constants to user config + xxhash64 cch signing
starbaser May 4, 2026
90234af
refactor: collapse oat_sources + transforms into unified `providers` map
starbaser May 4, 2026
94fa997
fix: sweep stale wireguard-* config files at inspector startup
starbaser May 5, 2026
f4d53f2
fix: detect sentinel key on any inbound auth header
starbaser May 5, 2026
94a218d
docs(claude+readme): document shape replay as load-bearing for Anthro…
starbaser May 5, 2026
af897a9
fix(inspector): use flow.id as conversation_id seed when first text e…
starbaser May 5, 2026
cd57ff4
feat(flows): add MitmwebClient.get_response_body
starbaser May 5, 2026
5f4bbcb
fix(gemini_capacity_fallback): defensive copy of request_body in retry
starbaser May 5, 2026
17768a9
fix(config): per-provider locks for OAuth refresh
starbaser May 5, 2026
2d30cd5
fix(gemini_cli): restrict _ACTION_RE to known cloudcode-pa actions
starbaser May 5, 2026
8e6569e
refactor(mcp/server): use new get_response_body method
starbaser May 5, 2026
eb2b90d
style(flows): move S607 noqa onto list literal after fmt reflow
starbaser May 5, 2026
08fd970
docs(readme+config): recommend providers.anthropic.auth pointed at ~/…
starbaser May 5, 2026
78aec37
refactor(oauth): collapse anthropic/google modules into AuthSource ba…
starbaser May 5, 2026
04cf89a
refactor(hooks): extract gemini_envelope.py with EnvelopeUnwrapStream…
starbaser May 5, 2026
da26d1d
perf(flows): cache parsed request body on FlowRecord
starbaser May 5, 2026
40762ca
refactor(inspector): extract OAuthAddon for response-side 401 retry
starbaser May 5, 2026
bfe522a
refactor(inspector): extract GeminiAddon for envelope unwrap
starbaser May 5, 2026
a148e48
refactor(inspector): absorb capacity fallback into GeminiAddon, disso…
starbaser May 5, 2026
a4f3c41
chore(config): drop legacy gemini_capacity_fallback hook check
starbaser May 5, 2026
f41a268
docs: clarify AuthFields vs AuthSource, document Gemini prewarm_proje…
starbaser May 5, 2026
c1fa30e
style: ruff format sweep
starbaser May 5, 2026
ccfa7e2
docs(config): refresh configuration reference, nest readiness probe u…
starbaser May 5, 2026
7353065
docs(claude): regenerate CLAUDE.md, expand dev/prod section
starbaser May 5, 2026
3588032
docs(sdk): add sdk optional deps, gemini/deepseek/lightllm examples, …
starbaser May 5, 2026
1a846fc
fix(transform): repair OpenAI↔Gemini cross-format pipeline
starbaser May 5, 2026
ce21e3f
refactor(oauth): disk-as-truth credential resolution, fix capacity-fa…
starbaser May 5, 2026
f3706c3
feat: add 500 INTERNAL to retry_status_codes for gemini fallback
starbaser May 6, 2026
f00595e
chore: inline forAllSystems and rename wheelFixes to pyprojectOverrides
starbaser May 7, 2026
6b3772c
feat(gemini): inject request.session_id for cloudcode-pa implicit pre…
starbaser May 8, 2026
118a6cb
chore: prune redundant tests and bump flake.lock
starbaser May 8, 2026
d38655e
chore: add curl-cffi dependency to pyproject.toml
starbaser May 9, 2026
088f103
chore(ccproxy): suppress mitmproxy.proxy.server logs in setup_logging
starbaser May 9, 2026
6e672c6
docs: audit doc set against current code, prune vendored litellm refs
starbaser May 9, 2026
e909b5e
refactor(ccproxy): change _render_signature to one-param-per-line format
starbaser May 10, 2026
11e4f04
feat: add PerplexityProConfig for cookie-auth WebUI subscription
starbaser May 10, 2026
88f6674
feat(transport): fingerprint-aware outbound via curl-cffi sidecar
starbaser May 12, 2026
119782a
fix(transport): pass log_config=None to uvicorn so it stops closing r…
starbaser May 12, 2026
331614e
feat(ci): validate non-Nix pip install on Linux + macOS via container…
starbaser May 12, 2026
1f1058a
chore: disable validate-install-macos job to reduce CI costs
starbaser May 12, 2026
ed0ecab
chore: pass CCPROXY_CONFIG_DIR to ccproxy process environment
starbaser May 13, 2026
6fa723a
feat(pplx): promote Perplexity Pro to spec-complete production support
starbaser May 13, 2026
6cd1ab3
chore(flake): regenerate ccproxy.yaml template from dev shell, drop p…
starbaser May 13, 2026
c6ba9c5
refactor(pplx): drop leading underscore from PascalCase class names
starbaser May 13, 2026
0f95982
chore: restructure tool.ty config to tool.ty.environment section
starbaser May 14, 2026
d1ab704
feat(mcp): host FastMCP streamable-HTTP server in daemon; remove stdi…
starbaser May 14, 2026
d637e19
style: apply ruff format across previously-untouched files
starbaser May 14, 2026
35d6250
feat(ccproxy): add pplx_usage tool with 60s TTL cache
starbaser May 14, 2026
0db9a3d
flake.nix
starbaser May 14, 2026
1e609c3
refactor(ccproxy): disable websockets in run_inspector uvicorn config
starbaser May 15, 2026
bf59dbc
refactor(ccproxy): extract Perplexity MCP tools to ccpplx; rename ccp…
starbaser May 17, 2026
2f901af
feat(ccproxy): add GET /pplx/messages endpoint for session resume
starbaser May 18, 2026
05777b1
feat(ccproxy): render Perplexity SSE step trail to OpenAI clients
starbaser May 19, 2026
7bc5d0b
refactor(ccproxy): use explicit timeout in _attempt_request
starbaser May 19, 2026
a7a583f
refactor(ccproxy): replace _extract_final_answer with block-based parser
starbaser May 20, 2026
f7613b0
feat(ccproxy): introduce pydantic-ai-mediated wire layer in lightllm/
starbaser May 21, 2026
b8469d7
feat(ccproxy): add SsePipeline, buffered renderer, and TransformMeta …
starbaser May 21, 2026
1d9210c
feat(ccproxy): rewire inspector to use pydantic-ai-mediated wire layer
starbaser May 21, 2026
7e3670d
refactor(ccproxy): migrate Context typed properties to IR, delete wir…
starbaser May 21, 2026
14b8904
fix(ccproxy): worker-thread fallback for sync IR bridges in async hooks
starbaser May 21, 2026
d95834d
cleaned up old plan files
starbaser May 21, 2026
6a24456
refactor(ccproxy): migrate lightllm wire layer to pydantic-graph FSM
starbaser May 21, 2026
52f6037
refactor: rename CLAUDE.md → AGENTS.md; CLAUDE.md imports via @
starbaser May 21, 2026
04cefaf
Rename Sse* to SSE*
starbaser May 21, 2026
1d8407e
refactor(ccproxy): replace node classes with graphbuilder steps
starbaser May 21, 2026
e2834e8
refactor(ccproxy): replace user-turn nodes with GraphBuilder functions
starbaser May 21, 2026
25d2564
chore: disable mypy errors for pydantic_graph TypeVar inference
starbaser May 21, 2026
488c876
refactor(ccproxy): migrate response side to pydantic-graph FSM; remov…
starbaser May 22, 2026
838ace5
refactor(ccproxy): replace lightllm FSM load/dump with UIAdapters
starbaser May 22, 2026
b495e02
refactor(ccproxy): Google/Perplexity adapters + graph_ext + HookResult
starbaser May 22, 2026
aa20968
refactor(ccproxy): pin pydantic-graph 1.99+; Context as LLMRenderInput
starbaser May 22, 2026
19c441e
docs(lightllm): fix stale FSM example + GoogleAdapter description
starbaser May 22, 2026
4d914a9
fix(shaping): invalidate Context IR cache after apply_shape stamps body
starbaser May 23, 2026
2301846
feat(lightllm): Phase F/H — subgraph composition + typed tool promotion
starbaser May 23, 2026
633cab5
chore(lightllm): drop dead PerplexityProConfig + registry; doc cleanup
starbaser May 23, 2026
edb498f
refactor(ccproxy): InboundFormat rename + Context.extras + HookDAG.re…
starbaser May 23, 2026
5cf5627
docs(AGENTS): compress + dedupe (~24% reduction, 585→442 lines)
starbaser May 23, 2026
c6b3bc8
feat(ccproxy): add OpenAIResponsesAdapter for /v1/responses
starbaser May 24, 2026
f232fe9
feat(ccproxy): add OpenAIResponsesRenderFSM for Codex CLI support
starbaser May 24, 2026
c9a4378
Preserve Perplexity data surfaces
starbaser May 24, 2026
95ca5b9
feat(ccproxy): add FlowsRepl and bundled default shapes
starbaser May 24, 2026
426e831
refactor(ccproxy)!: replace patches_dir with unified shapes_dir layout
starbaser May 24, 2026
59a403c
feat(ccproxy): add CapturedFingerprint for shape-backed profiles
starbaser May 24, 2026
d697098
Centralize ccproxy flow metadata access
starbaser May 24, 2026
4a9af8f
docs: replace flow.metadata references with ctx.metadata API
starbaser May 24, 2026
cfeea75
feat(ccproxy): tighten shape capture, add bundled scrubber + egress s…
starbaser May 25, 2026
588ae09
fix(ccproxy): scrubber deletes keys instead of zero-placeholders, cle…
starbaser May 25, 2026
e16281b
revert(ccproxy): drop bundled-shape scrubber — wrong design
starbaser May 25, 2026
24ce23c
Generalize auth injection and package shapes
starbaser May 25, 2026
a8f3cc0
chore: sync template workflow and add pre-commit refresh hook
starbaser May 26, 2026
3540d0f
Package public default shapes
starbaser May 26, 2026
faf01f2
Trim stale handoff follow-ups
starbaser May 26, 2026
fbf3f41
Update docs for auth and shape packaging
starbaser May 26, 2026
a24cdf1
Rename sidecar relay header filter
starbaser May 26, 2026
a12fec6
Reframe shape docs around packaged defaults
starbaser May 26, 2026
4b9afef
Add manual stale-shape recovery guide
starbaser May 26, 2026
c16294e
Clarify local shape save preparation
starbaser May 26, 2026
57d9fb9
refactor(ccproxy): clear stale params in load_hooks between calls
starbaser May 26, 2026
540879b
Consolidate SDK examples
starbaser May 29, 2026
44d6e96
Fix sidecar stream decoding for commitbee
starbaser May 29, 2026
38e0deb
Add namespace privacy transparency tools
starbaser Jun 6, 2026
ea09d98
Add WSL2 release artifact support
starbaser Jun 6, 2026
aa1c4f1
feat(scripts): add wsl_kvm_smoke.sh for automated windows testing
starbaser Jun 6, 2026
f72725e
Fix sidecar provider timeout handling
starbaser Jun 6, 2026
2da07e8
chore(plugin): remove bundled ccproxy mcp registration
starbaser Jun 11, 2026
f629c64
refactor: remove inspector and lib repository definitions from kitstore
starbaser Jun 11, 2026
b42c02b
docs: remove wsl2.md and update shape capture commands to ccproxy shapes
starbaser Jun 11, 2026
3045e48
docs(scripts): update command in _print_rich help text
starbaser Jun 11, 2026
b2e2d24
chore: remove ccproxy_mcp command validation from CI workflow
starbaser Jun 11, 2026
e9de479
fix: 2.0 release readiness — mount /mcp/notify, unify MCP socket, hon…
starbaser Jun 11, 2026
aa2a181
Add OpenAI Responses graph intake
starbaser Jun 12, 2026
11c91c4
refactor(ccproxy): replace Any with precise type aliases in graphs
starbaser Jun 12, 2026
e3fd2cf
Tighten ccproxy typing boundaries
starbaser Jun 12, 2026
c99c404
Add packaged Codex shape support
starbaser Jun 12, 2026
70902e4
Finalize 2.0.0 changelog
starbaser Jun 12, 2026
174d379
Improve WSL KVM smoke diagnostics
starbaser Jun 12, 2026
22f03cb
Stabilize WSL KVM smoke harness
starbaser Jun 13, 2026
db6fefe
Rename PyPI distribution to ai-ccproxy
starbaser Jun 13, 2026
12342ad
Expand typecheck gate to tests
starbaser Jun 13, 2026
11d6e43
style: reformat nix/defaults.nix arrays to multi-line
starbaser Jun 14, 2026
b14633b
refactor: replace pre-commit hook with .githooks/pre-commit script
starbaser Jun 14, 2026
1c1c4f7
Drop table rendering from flows list
starbaser Jun 14, 2026
243a56d
Preserve pass-through request bodies
starbaser Jun 14, 2026
8353fcd
Validate flows jq selectors
starbaser Jun 14, 2026
698c423
Move lightllm transforms out of inspector config
starbaser Jun 18, 2026
71ed417
refactor: remove lightllm key from ccproxy.yaml inspector config
starbaser Jun 18, 2026
2fa4a61
docs: rename inspector.transforms to lightllm.transforms in all docs
starbaser Jun 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
docs(lightllm): fix stale FSM example + GoogleAdapter description
- The FSM pattern section used invented dump-side symbol names
  (AnthropicDumpState, parse_text, _DumpDone, apply_cache, _dump_graph,
  render_anthropic_dump) that don't exist in the codebase. Replaced with
  the real anthropic_intake.py shape (_AnthropicIntakeState,
  frame_next_event, handle_content_block_*, _FeedDone, _IgnoredEvent,
  _intake_graph, AnthropicResponseIntakeFSM.feed). Reframed to make clear
  the FSM idiom is response-side only; request side is procedural adapter
  classmethods.
- GoogleAdapter description claimed it wraps pydantic-ai's GoogleModel.
  It doesn't — it does direct generateContent wire construction
  (camelCase keys, base64 inline data, generationConfig hoist).
- Roundtrip test snippet showed AnthropicAdapter.load_messages returning
  a (messages, settings, raw_extras) tuple. Actual signature returns
  list[ModelMessage]; settings and raw_extras come from envelope helpers
  and are passed through via raw_extras kwarg.
- Visualization example imported _dump_graph from anthropic_dump (deleted
  module). Replaced with _intake_graph from anthropic_intake and listed
  the other graph names.
- Lossiness invariants section dropped the obsolete "pre-FSM wire.py
  predecessor" reference; rewrote to describe the current adapter
  contract instead.
- File map deduplicated the SSE pipeline row.
  • Loading branch information
starbaser committed May 22, 2026
commit 19c441e9174d8471b53194cb79705d251f60aa4f
262 changes: 153 additions & 109 deletions docs/lightllm.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,93 +205,127 @@ to is a separate decision (made by the transform router via sentinel-key or

---

## The FSM pattern
## The FSM pattern (response side only)

Every file under `lightllm/graph/*_intake.py` and `*_render.py` follows the
same shape. These handle streaming response transformations. Reading
`anthropic_intake.py` end-to-end is the fastest way to understand it; the
other modules echo its idioms.

**Graph builder import**: pydantic-graph >=1.99.0 uses canonical paths:
The four `lightllm/graph/*_intake.py` modules and two `*_render.py` modules
share a single shape. These handle **streaming SSE** transformations and are
the only place ccproxy still uses pydantic-graph at runtime — the request
side is procedural adapter classmethods, not graphs. Reading
`anthropic_intake.py` end-to-end is the fastest way to understand the idiom;
the other modules echo it.

```python
from pydantic_graph import GraphBuilder, StepContext
from pydantic_graph import GraphBuilder, StepContext # canonical, not .beta

# 1. State — a mutable dataclass carrying everything the FSM needs across steps.
@dataclass
class AnthropicDumpState:
queue: deque[Any] = field(default_factory=deque)
blocks: list[BetaContentBlockParam] = field(default_factory=list)
last_emitted_block: BetaContentBlockParam | None = None

# 2. End-of-graph sentinel — a marker class routed to a terminal step.
class _DumpDone:
"""Marker returned when the queue is exhausted."""

# 3. GraphBuilder — type parameters describe the FSM's runtime signature.
_g: GraphBuilder[AnthropicDumpState, None, None, list[BetaContentBlockParam]] = GraphBuilder(
state_type=AnthropicDumpState,
output_type=list[BetaContentBlockParam],
class _AnthropicIntakeState:
parts_manager: ModelResponsePartsManager
provider_name: str
current_block: BetaContentBlock | None = None
events_queue: deque[BetaRawMessageStreamEvent] = field(default_factory=deque)
out_events: list[ModelResponseStreamEvent] = field(default_factory=list)
# ... per-FSM extra fields

# 2. Marker classes — sentinel values the decision routes on.
class _FeedDone: ... # queue exhausted; route to terminal step
class _IgnoredEvent: ... # event has no IR equivalent; loop back to router

# 3. GraphBuilder — type parameters: [state, deps, inputs, output].
_g: GraphBuilder[
_AnthropicIntakeState, None, None, list[ModelResponseStreamEvent]
] = GraphBuilder(
state_type=_AnthropicIntakeState,
output_type=list[ModelResponseStreamEvent],
)

# 4. Router step — pops the next item OR signals done.
# 4. Router step — pops the next typed event OR signals done.
@_g.step
async def take_next(ctx: StepContext[AnthropicDumpState, None, None]) -> Any:
if not ctx.state.queue:
return _DumpDone()
return ctx.state.queue.popleft()

# 5. Per-type handler steps — one per IR-part type.
async def frame_next_event(ctx: StepContext[_AnthropicIntakeState, None, None]) -> Any:
state = ctx.state
while state.events_queue:
event = state.events_queue.popleft()
if isinstance(event, (BetaRawMessageStartEvent, BetaRawMessageDeltaEvent)):
return _IgnoredEvent()
if isinstance(event, BetaRawMessageStopEvent):
state.current_block = None
return _IgnoredEvent()
return event
return _FeedDone()

# 5. Per-variant handler steps — one per concrete BetaRaw*Event subclass.
@_g.step
async def parse_text(ctx: StepContext[AnthropicDumpState, None, str]) -> None:
block: BetaTextBlockParam = {"type": "text", "text": ctx.inputs}
ctx.state.blocks.append(block)
ctx.state.last_emitted_block = block
async def handle_content_block_start(
ctx: StepContext[_AnthropicIntakeState, None, BetaRawContentBlockStartEvent],
) -> None:
# ... drive ctx.state.parts_manager and append to ctx.state.out_events
...

# ... (more per-type steps for BinaryContent, ImageUrl, ToolReturnPart, etc.)
# (handle_content_block_delta, handle_content_block_stop, skip_ignored_event,
# emit_done all follow the same shape)

# 6. Terminal step — pulls the result out of state and hands it to end_node.
# 6. Terminal step — pulls the accumulated output out of state.
@_g.step
async def emit_blocks(ctx: StepContext[AnthropicDumpState, None, _DumpDone]) -> list[BetaContentBlockParam]:
return ctx.state.blocks
async def emit_done(
ctx: StepContext[_AnthropicIntakeState, None, _FeedDone],
) -> list[ModelResponseStreamEvent]:
return ctx.state.out_events

# 7. Wire the topology — declarative edges with a single decision fan-out.
_g.add(
_g.edge_from(_g.start_node).to(take_next),
_g.edge_from(take_next).to(
_g.edge_from(_g.start_node).to(frame_next_event),
_g.edge_from(frame_next_event).to(
_g.decision()
.branch(_g.match(_DumpDone).to(emit_blocks))
.branch(_g.match(str).to(parse_text))
.branch(_g.match(BinaryContent).to(parse_binary))
# ... per-IR-part-type branches
.branch(_g.match(_FeedDone).to(emit_done))
.branch(_g.match(_IgnoredEvent).to(skip_ignored_event))
.branch(_g.match(BetaRawContentBlockStartEvent).to(handle_content_block_start))
.branch(_g.match(BetaRawContentBlockDeltaEvent).to(handle_content_block_delta))
.branch(_g.match(BetaRawContentBlockStopEvent).to(handle_content_block_stop))
),
# Loop-back: every parse_* step feeds back into take_next.
_g.edge_from(parse_text, parse_binary, ...).to(take_next),
_g.edge_from(emit_blocks).to(_g.end_node),
# Loop-back: every handler step feeds back into the router.
_g.edge_from(
handle_content_block_start,
handle_content_block_delta,
handle_content_block_stop,
skip_ignored_event,
).to(frame_next_event),
_g.edge_from(emit_done).to(_g.end_node),
)

# 8. Build once at import time.
_dump_graph = _g.build()

# 9. Public entrypoint — drives the graph from imperative wrapper code.
async def render_anthropic_dump(parsed: ParsedRequest) -> bytes:
# ... assemble static envelope (model, tools, system, settings, raw_extras)
state = AnthropicDumpState(queue=deque(flatten_messages_to_items(parsed.messages)))
blocks = await _dump_graph.run(state=state)
# ... stitch blocks into the BetaMessageParam list and serialize
return json.dumps(body, separators=(",", ":")).encode()
_intake_graph = _g.build()

# 9. Public FSM wrapper — drives the graph per chunk of SSE bytes.
class AnthropicResponseIntakeFSM:
def __init__(self, *, model: str, request_params: ModelRequestParameters):
self._state = _AnthropicIntakeState(
parts_manager=ModelResponsePartsManager(model_request_parameters=request_params),
provider_name="anthropic",
)

async def feed(self, data: bytes) -> list[ModelResponseStreamEvent]:
# parse SSE frames out of the buffer, push typed events onto the
# state's events_queue, then run the graph
...
self._state.out_events = []
result = await _intake_graph.run(state=self._state)
return result
```

The render side (`anthropic_render.py`, `openai_render.py`) is symmetric:
state owns an `events_queue: deque[ModelResponseStreamEvent]` and an
`out_bytes: bytearray`; handler steps emit SSE wire bytes per IR event;
the terminal step returns `bytes(state.out_bytes)`.

### Why this shape

| Concern | Solution |
|---|---|
| **Polymorphic walk** over heterogeneous IR parts | One router step (`take_next`) + a decision with a branch per type. |
| **End-of-graph from a router** | A marker class (e.g. `_DumpDone`) routed via `g.match(_DumpDone).to(terminal_step)`. The terminal step returns the accumulated state — that value becomes the graph's output. |
| **Typed dispatch on string-discriminated unions** (load + intake side) | Wrap the runtime-string-tagged dicts in one frozen dataclass per discriminator value (`_UserTextBlock`, `_MessageStartEvent`, …). The router inspects the discriminator once and emits the matching envelope; the decision routes by Python type. |
| **Centralized middleware** (e.g. `cache_control` attachment) | A dedicated step that mutates state side-effectfully. Every other step that emits a block updates a `state.last_emitted_block` reference; the middleware step mutates the dict that reference points to. |
| **Side-effect-only no-ops** | A `skip_item` step matched by a `_Skip` marker that loops back to the router. |
| **Mermaid visualization** | Free via `graph.render(title=..., direction='LR')`. Every FSM file can produce its diagram on demand. |
| **Polymorphic walk** over heterogeneous typed events | One router step (`frame_next_event`) + a decision with a branch per concrete event class. |
| **End-of-graph from a router** | A marker class (`_FeedDone`) routed via `g.match(_FeedDone).to(emit_done)`. The terminal step returns the accumulated state — that value becomes the graph's output. |
| **Events with no IR output** (e.g. `message_start`, `message_delta`) | A `_IgnoredEvent` marker matched to a `skip_ignored_event` step that loops back to the router. |
| **Per-chunk drive** | `feed(data)` parses SSE frames out of an internal buffer into typed events, clears `state.out_events`, runs the graph once, returns the accumulated IR events. State persists across chunks (current block, parts_manager, etc.). |
| **Mermaid visualization** | Free via `graph.render(title=..., direction='LR')`. See the Visualization section below. |

### What each file does

Expand All @@ -301,8 +335,8 @@ async def render_anthropic_dump(parsed: ParsedRequest) -> bytes:
|---|---|
| `anthropic.py` | `AnthropicAdapter` — bidirectional wire ↔ IR for Anthropic Messages |
| `openai_chat.py` | `OpenAIChatAdapter` — bidirectional wire ↔ IR for OpenAI Chat Completions |
| `google.py` | `GoogleAdapter` — outbound-only IR → Google Gemini wire (wraps pydantic-ai's `GoogleModel`) |
| `perplexity.py` | `PerplexityAdapter` — outbound-only IR → Perplexity Pro wire (wraps `pplx.py` helpers) |
| `google.py` | `GoogleAdapter` — outbound-only IR → Google Gemini `generateContent` wire bytes. Direct dict construction with camelCase keys, base64-inline binary data, `generationConfig` hoist for sampling params. Does NOT wrap pydantic-ai's `GoogleModel` — too many ccproxy-specific tweaks (cloudcode-pa envelope, raw_extras passthrough). |
| `perplexity.py` | `PerplexityAdapter` — outbound-only IR → Perplexity Pro wire bytes. Projects IR back to OpenAI-format dicts, then invokes `pplx.py:_build_pplx_payload` (the 28-field Perplexity payload builder) with `raw_extras["pplx"]` as the params block. |
| `_envelope.py` | `parse_request_into_fields`, `parse_request`, `render_request` — test/inspector helpers |
| `_anthropic_envelope.py` | Anthropic wire helpers |
| `_openai_envelope.py` | OpenAI wire helpers |
Expand Down Expand Up @@ -530,13 +564,16 @@ the outbound renderer produces a wire body — the round-trip should be
tests assert this via canonicalization helpers
(`assert_anthropic_bodies_equivalent`) for every shape in the test corpus.

The lossiness regressions specifically called out:
* `ToolReturnPart.tool_name` populated via two-pass lookup (was hardcoded
to `""` in the pre-FSM wire.py predecessor).
* Image `media_type` preserved on `BinaryContent` (was defaulted).
* `cache_control` TTLs pydantic-ai can't represent stashed in `raw_extras`
(were silently coerced).
* Unknown content blocks preserved in `raw_extras` (were dropped).
The lossiness invariants specifically called out:
* `ToolReturnPart.tool_name` populated via the adapter's two-pass lookup
(scan assistant turns to build `{tool_use_id: tool_name}`, then attach
during user-turn `tool_result` parsing).
* Image `media_type` preserved on `BinaryContent` (no default-fallback).
* `cache_control` TTLs pydantic-ai's `CachePoint` can't represent (anything
other than `5m` / `1h`) stashed in `raw_extras["cc:msg:N:block:M"]` and
re-applied verbatim by the adapter's `render()` path.
* Unknown content blocks (anything with an unrecognized `type`) preserved
in `raw_extras["unknown_block:msg:N:idx:M"]` and re-emitted on dump.

---

Expand Down Expand Up @@ -742,13 +779,25 @@ render file when the vendor is a listener format) for the FSMs:

`tests/test_lightllm_graph_anthropic_dump.py` and
`tests/test_lightllm_graph_anthropic_load.py` together assert the
roundtrip:
roundtrip. The pattern is: load body → IR via the adapter, wrap in a
`ParsedRequest` (or `Context`) test fixture, render back to wire bytes via
the adapter, then compare against the input:

```python
# Load wire → IR
messages, settings, raw_extras = AnthropicAdapter.load_messages(case.body)
# Rebuild wire from IR
req = ParsedRequest(model=..., messages=messages, settings=settings, raw_extras=raw_extras)
# Load wire → IR. raw_extras and settings come from envelope helpers;
# adapter.load_messages only returns the message stream.
raw_extras: dict[str, Any] = {}
messages = AnthropicAdapter.load_messages(
case.body["messages"], system=case.body.get("system"), raw_extras=raw_extras,
)
# In the test bench, build a ParsedRequest fixture with the full IR shape:
req = ParsedRequest(
model=case.body["model"],
messages=messages,
request_parameters=ModelRequestParameters(function_tools=...),
settings=settings,
raw_extras=raw_extras,
)
rendered = AnthropicAdapter.render(req)
rebuilt = json.loads(rendered)
assert_anthropic_bodies_equivalent(case.body, rebuilt)
Expand Down Expand Up @@ -801,52 +850,48 @@ Mirror these for any new provider's adapter.

## Visualization

Every FSM in `lightllm/graph/` can render itself as a mermaid diagram:
Every built FSM in `lightllm/graph/` exposes a `.render()` mermaid
generator. Import the private module-level graph and print the diagram:

```python
from ccproxy.lightllm.graph.anthropic_dump import _dump_graph
print(_dump_graph.render(title="anthropic_dump", direction="LR"))
from ccproxy.lightllm.graph.anthropic_intake import _intake_graph
print(_intake_graph.render(title="anthropic_intake", direction="LR"))
```

Produces (excerpt):

```
---
title: anthropic_dump
title: anthropic_intake
---
stateDiagram-v2
direction LR
take_next
frame_next_event
state decision <<choice>>
apply_cache
emit_blocks
parse_binary
parse_text
parse_tool_call_part
parse_tool_return
parse_url
skip_item

[*] --> take_next
take_next --> decision
decision --> apply_cache
decision --> emit_blocks
decision --> parse_binary
decision --> parse_text
decision --> parse_tool_call_part
decision --> parse_tool_return
decision --> parse_url
decision --> skip_item
apply_cache --> take_next
parse_binary --> take_next
parse_text --> take_next
parse_tool_call_part --> take_next
parse_tool_return --> take_next
parse_url --> take_next
skip_item --> take_next
emit_blocks --> [*]
emit_done
handle_content_block_delta
handle_content_block_start
handle_content_block_stop
skip_ignored_event

[*] --> frame_next_event
frame_next_event --> decision
decision --> emit_done
decision --> handle_content_block_start
decision --> handle_content_block_delta
decision --> handle_content_block_stop
decision --> skip_ignored_event
handle_content_block_start --> frame_next_event
handle_content_block_delta --> frame_next_event
handle_content_block_stop --> frame_next_event
skip_ignored_event --> frame_next_event
emit_done --> [*]
```

The render-side graph lives at `_render_graph` in `anthropic_render.py`;
likewise `openai_intake._intake_graph`, `openai_render._render_graph`,
`google_intake._intake_graph`, `perplexity_intake._intake_graph`.

Useful for debugging surprising routing, for code reviews, and for
keeping docs in sync.

Expand Down Expand Up @@ -932,9 +977,8 @@ envelope without unwrap).
| OpenAI response FSMs | `src/ccproxy/lightllm/graph/openai_{intake,render}.py` |
| Google response FSM | `src/ccproxy/lightllm/graph/google_intake.py` |
| Perplexity response FSM | `src/ccproxy/lightllm/graph/perplexity_intake.py` |
| Streaming response pipeline | `src/ccproxy/lightllm/graph/sse_pipeline.py` |
| Buffered response transform | `src/ccproxy/lightllm/graph/buffered.py` |
| Persistent-loop bridge (response stream) | `src/ccproxy/lightllm/graph/sse_pipeline.py:SSEPipeline` |
| Streaming response pipeline (persistent-loop bridge) | `src/ccproxy/lightllm/graph/sse_pipeline.py:SSEPipeline` |
| Buffered response transform | `src/ccproxy/lightllm/graph/buffered.py:transform_buffered_response_sync` |
| Inspector streaming call site | `src/ccproxy/inspector/addon.py:_install_streaming_transformer` |
| Inspector buffered call site | `src/ccproxy/inspector/routes/transform.py:handle_transform_response` |
| Inspector transform call site | `src/ccproxy/inspector/routes/transform.py:_handle_transform` |
Expand Down