feat(llm): send an explicit reasoning_effort for Kimi models that support it (GRAPHIFY_KIMI_EFFORT) - #2982
feat(llm): send an explicit reasoning_effort for Kimi models that support it (GRAPHIFY_KIMI_EFFORT)#2982Azeem1985 wants to merge 21 commits into
Conversation
…ly authenticated Codex CLI
Sibling of the existing claude-cli backend, for operators whose OpenAI access is a ChatGPT
subscription (Codex CLI OAuth), not an API key. Ported from a field-proven implementation
(measured ~308 docs/hour at concurrency 6 on an 8 GB box): codex exec --skip-git-repo-check
--json --sandbox read-only, prompt via stdin (argv would hit Linux MAX_ARG_STRLEN), result
via -o file, empty-graph refusal to avoid hollow-bisect storms. Two per-call config pins:
mcp_servers={} (each exec otherwise spawns the user's configured MCP servers per call —
measured ~160-300 MB each, more than the extraction itself) and model_reasoning_effort
(GRAPHIFY_OPENAI_CLI_EFFORT, default ultra). Model via GRAPHIFY_OPENAI_CLI_MODEL (default
gpt-5.6-sol). Tests: argv contract, env overrides, missing-binary error.
…KIMI_EFFORT, default max) for models that support it K3 advertises valid_efforts [low, high, max]; with nothing sent the server applies its own default (high). The key is read by the existing kimi request plumbing and forwarded as reasoning_effort; env-overridable so no source edit is ever needed.
…the credential (OAuth), mirroring claude-cli
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 5 advisory finding(s) below merit a look before merge.
Formal verification. 2 change(s) alter behavior, breaking input(s) attached.
Behavior changes: \_\_getattr\_\_ changes behavior, here is the input that shows it.
The verifier found a concrete input on which \_\_getattr\_\_ behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.
Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.
Evidence: On input \{"name":"'save\_query\_result'"\}, the old code produced raises AttributeError but the new code produces \<function save\_query\_result at 0x7fc7479a2340\>. Paste that input straight into a regression test.
Behavior changes: run\_benchmark changes behavior, here is the input that shows it.
The verifier found a concrete input on which run\_benchmark behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.
Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.
Evidence: On input \{"graph\_path":"None","corpus\_words":"None","questions":"None"\}, the old code produced raises TypeError but the new code produces \{'corpus\_tokens': 990333, 'corpus\_words': 742750, 'nodes': 14855, 'edges': 27498, 'avg\_query\_tokens': 6343, 'reduction\_ratio': 156\.1, 'per\_question': \[\{'question': 'how does authentication…. Paste that input straight into a regression test.
Graphify review — findings
Adds full CI/CD and repo-hygiene scaffolding: GitHub Actions workflows for tests, skillgen drift checks, security scanning, PyPI trusted publishing, and a self-graph release asset, plus .dockerignore, .gitattributes (Linguist HTML vendoring), .pre-commit-config.yaml, FUNDING.yml, and expanded .gitignore. Regenerates the committed skillgen artifacts and their blessed expected outputs across the graphify/skills/ and tools/skillgen/expected/ trees, along with the associated test fixtures and rationale snapshots. Touches extractor internals (graphify_extract, C#/Swift/Common Lisp resolvers) and callflow HTML generation as part of the same pass.
Worth a look
- PyPI publish job uses an unpinned third-party action with OIDC publish privileges —
.github/workflows/publish.yml:54· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Runtime image copies entire build context —
Dockerfile:12· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Privacy docs claim query logging is enabled by default despite env table saying it is opt-in —
README.md· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- README gives conflicting default contract for query logging —
README.md· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Documented upgrade command uses misspelled package name —
README.md· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 14532 functions depend on the 14532 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 474 callers, 42 callees - new:
_rebuild_code()— 98 callers, 50 callees - new:
build_from_json()— 185 callers, 18 callees - new:
detect()— 108 callers, 15 callees - new:
deduplicate_entities()— 63 callers, 21 callees - new:
build_merge()— 46 callers, 14 callees - new:
save_semantic_cache()— 55 callers, 11 callees - new:
_extract_generic()— 18 callers, 24 callees - …and 215 more — each is listed as a finding
Verification — 14532 functions in the blast radius were not formally verified this run (proofs are advisory here).
Health delta baseline: last indexed commit b2cd362 (diverged from this PR's base — delta is approximate).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 14532 function(s) in the blast radius were not formally verified this run
Formal verification
Behavior changes: \_\_getattr\_\_ changes behavior, here is the input that shows it.
The verifier found a concrete input on which \_\_getattr\_\_ behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.
Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.
Evidence: On input \{"name":"'save\_query\_result'"\}, the old code produced raises AttributeError but the new code produces \<function save\_query\_result at 0x7fc7479a2340\>. Paste that input straight into a regression test.
Behavior changes: run\_benchmark changes behavior, here is the input that shows it.
The verifier found a concrete input on which run\_benchmark behaves differently before and after the change. If that change is intended, ship it; if not, this is your bug.
Guarantee: This difference was REPRODUCED, the verifier actually ran both versions on that input and saw them disagree. It is real, not an artifact.
Evidence: On input \{"graph\_path":"None","corpus\_words":"None","questions":"None"\}, the old code produced raises TypeError but the new code produces \{'corpus\_tokens': 990333, 'corpus\_words': 742750, 'nodes': 14855, 'edges': 27498, 'avg\_query\_tokens': 6343, 'reduction\_ratio': 156\.1, 'per\_question': \[\{'question': 'how does authentication…. Paste that input straight into a regression test.
No difference found (not proven): No behavior difference found in main (not a proof).
The verifier ran both versions of main on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
No difference found (not proven): No behavior difference found in \_cross\_community\_surprises (not a proof).
The verifier ran both versions of \_cross\_community\_surprises on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
No difference found (not proven): No behavior difference found in \_cross\_file\_surprises (not a proof).
The verifier ran both versions of \_cross\_file\_surprises on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
No difference found (not proven): No behavior difference found in \_file\_category (not a proof).
The verifier ran both versions of \_file\_category on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
Could not verify: Could not verify \_is\_file\_node.
The verifier did not have enough to check \_is\_file\_node, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: non-vacuity: domain too small (only 1 distinct inputs exercised, need 3) — 'no divergence' would be near-vacuous
Could not verify: Could not verify \_surprise\_score.
The verifier did not have enough to check \_surprise\_score, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 200 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly TypeError — names the real obstacle, not a sampling gap)
No difference found (not proven): No behavior difference found in god\_nodes (not a proof).
The verifier ran both versions of god\_nodes on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
Could not verify: Could not verify graph\_diff.
The verifier did not have enough to check graph\_diff, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: the input domain has 25 values but only 5 distinct were tested — a small finite domain must be EXHAUSTED, not sampled (an untested input could invert the result)
Could not verify: Could not verify suggest\_questions.
The verifier did not have enough to check suggest\_questions, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 200 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly ValueError — names the real obstacle, not a sampling gap)
No difference found (not proven): No behavior difference found in \_query\_subgraph\_tokens (not a proof).
The verifier ran both versions of \_query\_subgraph\_tokens on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
Could not verify: Could not verify print\_benchmark.
The verifier did not have enough to check print\_benchmark, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 6 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly KeyError — names the real obstacle, not a sampling gap)
Could not verify: Could not verify build.
The verifier did not have enough to check build, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 9 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly AttributeError — names the real obstacle, not a sampling gap)
Could not verify: Could not verify build\_from\_json.
The verifier did not have enough to check build\_from\_json, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 6 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly NameError — names the real obstacle, not a sampling gap)
Could not verify: Could not verify cache\_dir.
The verifier did not have enough to check cache\_dir, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify cached\_files.
The verifier did not have enough to check cached\_files, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify check\_semantic\_cache.
The verifier did not have enough to check check\_semantic\_cache, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify clear\_cache.
The verifier did not have enough to check clear\_cache, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify file\_hash.
The verifier did not have enough to check file\_hash, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify load\_cached.
The verifier did not have enough to check load\_cached, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify save\_cached.
The verifier did not have enough to check save\_cached, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify save\_semantic\_cache.
The verifier did not have enough to check save\_semantic\_cache, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set
No difference found (not proven): No behavior difference found in \_split\_community (not a proof).
The verifier ran both versions of \_split\_community on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
Could not verify: Could not verify cluster.
The verifier did not have enough to check cluster, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: non-vacuity: domain too small (only 2 distinct inputs exercised, need 3) — 'no divergence' would be near-vacuous
…ket mcp_servers={} does not work
Codex merges config overrides into the table rather than replacing it, so `-c mcp_servers={}` leaves
every server enabled — verified with Codex's own reader: `codex mcp list -c mcp_servers={}` still
shows 'enabled', while `-c mcp_servers.<name>.enabled=false` shows 'disabled' (`enabled` is a real
field in `codex mcp get`). Measured cost of the ineffective pin during a live extraction: four MCP
servers spawned by one `codex exec`, ~152 MB each.
Now the backend asks Codex for its own server list (`codex mcp list --json`) and adds one
`-c mcp_servers.<name>.enabled=false` per server — no hardcoded names, best-effort (older Codex or no
config: extraction proceeds unchanged). Tests cover both servers disabled, the empty-config case, and
that the blanket form is gone.
|
Re-targeted from |
Two `graphify extract` runs (or an extract racing a watcher/hook rebuild) on one graphify-out/ interleave cache saves and clobber graph.json. extract now takes the same advisory flock _rebuild_code takes, immediately after the output dir exists, and holds it to the end of the command. A contended run prints the holder's PID and waits up to GRAPHIFY_LOCK_TIMEOUT seconds (default 600) via a new bounded-wait mode on _rebuild_lock (the kernel only offers skip or wait-forever, so the deadline is polled in userspace), then exits 1 instead of hanging forever behind a wedged rebuild. The lock is acquired exactly once — flock is per-open-file-description, so re-entry in the same process would self-deadlock — and SystemExit unwinds the context manager, so the unlink-on-release contract holds on every exit path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… on a second backend When every semantic chunk fails on the primary backend (missing SDK package, bad key, an outage), a configured fallback backend retries the same still-uncached files once instead of failing the build. Nothing is cache-saved for a zero-success pass, so the retry covers exactly the files the primary failed on. --model belongs to the primary backend and is not forwarded, the fallback name is validated before any API spend, and only a zero-success retry keeps the all-chunks-failed exit 1. GRAPHIFY_FALLBACK_BACKEND sets the default; the CLI flag wins. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…doc changes Instead of only writing the needs_update flag, `graphify watch --semantic` runs `graphify extract` on the watched root when a debounced batch contains surviving non-code files. The extract runs as a subprocess on purpose: fcntl.flock is held per open file description, so in-process re-entry of _rebuild_lock would self-deadlock, while a child process serializes cleanly on graphify-out/.rebuild.lock against hook-driven rebuilds. A failed extract falls back to _notify_only so the user never loses the flag + /graphify --update instruction; a successful run clears the flag (extract itself never touches it — only _rebuild_code does). --backend and --fallback-backend are forwarded to the extract subprocess and rejected without --semantic rather than being a silent no-op. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The push sinks ran one MERGE query per node and per edge, so pushing a large graph to a non-local server spent nearly all its time on round trips. Rows are now grouped by sanitized node label / relationship type (those are interpolated into the Cypher text and cannot be parameters) and sent in UNWIND batches of --batch-size rows (default 100, ~100x fewer round trips). Each row carries exactly the old per-entry params and UNWIND processes rows in order, so the MERGE/SET upsert semantics - including idempotent re-runs - are unchanged. A non-positive or non-integer --batch-size is rejected on parse, before any connection. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review addition: the no-double-spend edge — when the primary backend got at least one semantic chunk through, --fallback-backend must NOT re-dispatch the whole uncached set on the second backend. Locks the succeeded == 0 trigger condition in place. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
extract: hold the per-repo rebuild lock for the whole pipeline
# Conflicts: # CHANGELOG.md # graphify/cli.py
extract: --fallback-backend retries a zero-success semantic pass on a second backend
# Conflicts: # CHANGELOG.md
watch: --semantic runs LLM extraction automatically when doc/paper/image files change
# Conflicts: # CHANGELOG.md
export: batched UNWIND push for neo4j/falkordb (--batch-size)
# Conflicts: # graphify/cli.py
…etail, and turn-usage accounting; changelog The argv-contract tests said what we send; these say what we accept back: the -o JSON becomes the result dict with usage from the last turn.completed JSONL event (cached input not double-counted), a non-zero exit or a missing/ empty -o file raises with stderr AND the JSONL stdout tail preserved (HTTP errors surface only there), and an empty-but-valid graph raises instead of feeding the hollow-response bisect. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
llm: openai-cli backend — semantic extraction through the locally authenticated Codex CLI
… default max; changelog Import-time env pattern, so the test reloads the module around each assertion — same convention as test_anthropic_custom_endpoint.py. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 5 advisory finding(s) below merit a look before merge.
Graphify review — findings
Adds an openai-cli extraction backend (Codex CLI via codex exec, stdin prompt, read-only sandbox, disabled MCP servers, serial by default) and forces it to concurrency 1 alongside ollama/claude-cli. Adds graphify extract --fallback-backend retry-on-second-backend, wraps the whole extract pipeline in the per-repo rebuild lock, and makes graphify watch --semantic run LLM extraction as a subprocess with forwarded --backend/--fallback-backend. Batches neo4j/falkordb --push into UNWIND queries with a new --batch-size (default 100), and sends an explicit reasoning_effort for the kimi backend.
Worth a look
- Crashed semantic pass with partial progress bypasses fallback and failure —
graphify/cli.py· Escalate · high- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- --no-cluster path now performs clustering and writes clustered graph schema —
graphify/cli.py· Escalate · high- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Cargo introspection call lost its local import —
graphify/cli.py· Escalate · high- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Non-Cargo extraction output path is nested under --cargo branch —
graphify/cli.py· Escalate · high- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Indentation shift moves _extraction_incomplete / AST extraction under needs_llm block —
graphify/cli.py· Escalate · high- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 1360 functions depend on the 621 functions this change touches.
Health — this change adds coupling hotspots:
- new:
_rebuild_code()— 98 callers, 50 callees - new:
deduplicate_entities()— 63 callers, 21 callees - new:
build_merge()— 46 callers, 14 callees - new:
extract_files_direct()— 17 callers, 21 callees - new:
extract_corpus_parallel()— 26 callers, 11 callees - new:
_call_claude_cli()— 31 callers, 9 callees - new:
dispatch_command()— 2 callers, 121 callees - new:
_call_llm()— 11 callers, 21 callees - …and 24 more — each is listed as a finding
Verification — 1360 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 1296 function(s) in the blast radius were not formally verified this run
· 4 grounded finding(s) anchored inline below; 28 more finding(s) on lines outside this diff (see the check run).
| yield rows[start:start + batch_size] | ||
|
|
||
|
|
||
| def push_to_neo4j( |
There was a problem hiding this comment.
push_to_neo4j()
6 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
| return " | ".join(parts) or "(no stderr or stdout)" | ||
|
|
||
|
|
||
| def _call_claude_cli(user_message: str, max_tokens: int = 8192, *, deep_mode: bool = False, images: list[_ImageRef] | None = None) -> dict: |
There was a problem hiding this comment.
_call_claude_cli()
fans out to 9 callees (efferent coupling); 31 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
| return args | ||
|
|
||
|
|
||
| def _call_openai_cli(user_message: str, max_tokens: int = 8192, *, deep_mode: bool = False, images: list[_ImageRef] | None = None) -> dict: |
There was a problem hiding this comment.
_call_openai_cli()
fans out to 9 callees (efferent coupling); 11 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
|
|
||
|
|
||
| def watch(watch_path: Path, debounce: float = 3.0) -> None: | ||
| def watch( |
There was a problem hiding this comment.
watch()
fans out to 8 callees (efferent coupling); 6 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
|
Closing this in favour of #3071, which is the same change on a branch cut from This PR was opened from a branch that also carried unrelated work from our fork, so its diff was 12 files of a six-line change plus the whole of our fork's in-flight work. That is my mistake in how I cut the branch, and it made this unreviewable. Nothing about the proposed change has altered; only the packaging. #3071 has the full rationale and measurements. Sorry for the extra noise, and thank you for your patience. |
What
K3 advertises
valid_efforts ["low","high","max"]on its /models endpoint; with nothing sent the server default (high) silently applies, while the gemini block already carries an effort setting. This addsreasoning_effortto the kimi backend config, read by the existing request plumbing and forwarded asreasoning_effort; env-overridable viaGRAPHIFY_KIMI_EFFORTso no source edit is ever needed. Default in this PR ismax— maintainers may prefer defaulting to unset; happy to adjust.