Skip to content

feat: support extra_headers for custom providers - #3706

Open
hopstreax wants to merge 1 commit into
Graphify-Labs:v8from
hopstreax:fix/3680-custom-provider-extra-headers
Open

hopstreax wants to merge 1 commit into
Graphify-Labs:v8from
hopstreax:fix/3680-custom-provider-extra-headers

Conversation

@hopstreax

Copy link
Copy Markdown
Contributor

Summary

Adds support for configuring custom request headers for OpenAI-compatible providers through providers.json.

This enables providers such as OpenCode Go that require additional headers like x-opencode-session and a custom User-Agent.

Changes

  • Added optional extra_headers support for custom providers.

  • Passes configured headers to the OpenAI client via default_headers.

  • Supports extra_headers in both OpenAI-compatible client construction paths:

    • _call_openai_compat
    • _call_llm
  • Filters authorization headers case-insensitively so provider configuration cannot override the API key managed by Graphify.

  • Preserves existing extra_body behavior.

  • Added regression tests covering header forwarding, filtering, and absent/empty configurations.

Example

{
  "opencode-go": {
    "base_url": "https://opencode.ai/zen/go/v1",
    "default_model": "deepseek-v4-flash",
    "env_key": "OPENCODE_API_KEY",
    "extra_headers": {
      "x-opencode-session": "<uuid>",
      "User-Agent": "graphify/0.9.63"
    }
  }
}

Verification

  • uv run pytest tests/test_llm_backends.py — 118 passed
  • Related provider/deduplication tests — 109 passed
  • uv run ruff check graphify/llm.py tests/test_llm_backends.py — passed
  • git diff --check — passed

Closes #3680.

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).

Formal verification. No changes could be formally verified in this run.


Graphify review — findings

No blocking issues surfaced.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 913 functions depend on the 321 functions this change touches.

Health — this change adds coupling hotspots:

  • new: deduplicate_entities() — 77 callers, 24 callees
  • new: build_merge() — 76 callers, 14 callees
  • new: extract_files_direct() — 18 callers, 20 callees
  • new: build() — 52 callers, 6 callees
  • new: _call_claude_cli() — 33 callers, 9 callees
  • new: extract_corpus_parallel() — 26 callers, 11 callees
  • new: _call_llm() — 14 callers, 19 callees
  • new: _call_openai_compat() — 28 callers, 9 callees
  • …and 16 more — each is listed as a finding

Verification — 913 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: 578 function(s) in the blast radius were not formally verified this run

Test selection

Test selection

41 of 290 test file(s) selected (14%) via static blast radius.

  • tests/test_backend_env_isolation.py — impact
  • tests/test_backend_extras.py — impact
  • tests/test_build.py — impact
  • tests/test_build_merge_dedup_scope.py — impact
  • tests/test_build_merge_hyperedges_and_prune.py — impact
  • tests/test_build_merge_shrink_guard.py — impact
  • tests/test_carried_hyperedge_remap.py — impact
  • tests/test_charmap_encoding.py — impact
  • tests/test_chunking.py — impact
  • tests/test_claude_cli_backend.py — impact
  • tests/test_corrupt_graph_json.py — impact
  • tests/test_cross_extension_reexport_self_cycle.py — impact
  • tests/test_dedup.py — impact
  • tests/test_dedup_remaps_hyperedges.py — impact
  • tests/test_dedup_survivor_richness.py — impact
  • tests/test_evidence_binding.py — impact
  • tests/test_file_slice.py — impact
  • tests/test_global_graph.py — impact
  • tests/test_go_qualified_resolution.py — impact
  • tests/test_hyperedge_member_shapes.py — impact
  • tests/test_image_vision.py — impact
  • tests/test_injection_sentinel_coverage.py — impact
  • tests/test_issue_3472_source_file_collision.py — impact
  • tests/test_label_retry.py — impact
  • tests/test_labeling.py — impact
  • tests/test_llm_backends.py — impact, changed-test
  • tests/test_llm_parser.py — impact
  • tests/test_llm_parser_reasoning.py — impact
  • tests/test_no_dedup_flag.py — impact
  • tests/test_non_string_node_ids.py — impact
  • tests/test_ollama.py — impact
  • tests/test_ollama_retry_cap.py — impact
  • tests/test_oversized_document_slicing.py — impact
  • tests/test_partial_cache.py — impact
  • tests/test_pdf_slicing.py — impact
  • tests/test_pdf_token_estimate.py — impact
  • tests/test_provider_registry.py — impact
  • tests/test_prs.py — impact
  • tests/test_prune_sweeps_orphans.py — impact
  • tests/test_semantic_fragment_sanitize.py — impact
  • tests/test_unverified_semantic_shrink.py — impact

Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.

Formal verification

Could not verify: Could not verify \_call\_llm.

The verifier did not have enough to check \_call\_llm, 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: no capturable inputs from the test suite; property tier: not verifiable: all 23 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)

Verification did not run: Verification did not run for \_call\_openai\_compat.

The verification could not execute (an environment/toolchain issue, not a statement about the code).

Guarantee: No guarantee, the check itself did not complete.

Note: Detail: harness produced no verdict (rc=124): timeout after 30s

Could not verify: Could not verify extract\_files\_direct.

The verifier did not have enough to check extract\_files\_direct, 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: no capturable inputs from the test suite; property tier: parameter `root` is annotated `Path` — outside the synthesizable primitive/collection set

· 1 grounded finding(s) anchored inline below; 23 more finding(s) on lines outside this diff (see the check run).

Comment thread graphify/llm.py
return cleaned if cleaned else None


def _call_openai_compat(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regression_call_openai_compat()

fans out to 9 callees (efferent coupling); 28 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Custom providers: support extra_headers in providers.json (required by OpenCode Go)

1 participant