mirror of
https://github.com/safishamsi/graphify.git
synced 2026-09-22 05:25:40 +00:00
honour GRAPHIFY_API_TIMEOUT in claude-cli and Anthropic SDK backends (#1112)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
830f0396d8
commit
75c4de7759
@@ -366,7 +366,7 @@ These are only needed for **headless / CI extraction** (`graphify extract`). Whe
|
||||
| `AWS_*` / `~/.aws/credentials` | AWS Bedrock — standard credential chain | `--backend bedrock` (no API key, uses IAM) |
|
||||
| `GRAPHIFY_MAX_WORKERS` | AST parallelism thread count | optional — also `--max-workers` flag |
|
||||
| `GRAPHIFY_MAX_OUTPUT_TOKENS` | Raise output cap for dense corpora | optional — e.g. `32768` for large files |
|
||||
| `GRAPHIFY_API_TIMEOUT` | HTTP timeout in seconds (default: 600) | optional — also `--api-timeout` flag |
|
||||
| `GRAPHIFY_API_TIMEOUT` | Per-call timeout in seconds for HTTP, claude-cli, and Anthropic SDK backends (default: 600) | optional — also `--api-timeout` flag |
|
||||
| `GRAPHIFY_FORCE` | Force graph rebuild even with fewer nodes | optional — also `--force` flag |
|
||||
| `GRAPHIFY_GOOGLE_WORKSPACE` | Auto-enable Google Workspace export | optional — set to `1` |
|
||||
| `GRAPHIFY_TRIAGE_BACKEND` | Backend for `graphify prs --triage` | optional — auto-detected from available keys |
|
||||
|
||||
+17
-13
@@ -214,6 +214,19 @@ def _resolve_max_tokens(default: int) -> int:
|
||||
pass
|
||||
return default
|
||||
|
||||
|
||||
def _resolve_api_timeout(default: float = 600.0) -> float:
|
||||
"""Honour GRAPHIFY_API_TIMEOUT env var override, else use default (seconds)."""
|
||||
raw = os.environ.get("GRAPHIFY_API_TIMEOUT", "").strip()
|
||||
if raw:
|
||||
try:
|
||||
v = float(raw)
|
||||
if v > 0:
|
||||
return v
|
||||
except ValueError:
|
||||
pass
|
||||
return default
|
||||
|
||||
_EXTRACTION_SYSTEM = """\
|
||||
You are a graphify semantic extraction agent. Extract a knowledge graph fragment from the files provided.
|
||||
Output ONLY valid JSON — no explanation, no markdown fences, no preamble.
|
||||
@@ -434,16 +447,7 @@ def _call_openai_compat(
|
||||
# default. Honour GRAPHIFY_API_TIMEOUT (seconds) for explicit override;
|
||||
# default to 600s, which is long enough for a 31B model on a 16k chunk
|
||||
# but still bounds runaway connections (issue #792 addendum).
|
||||
timeout_raw = os.environ.get("GRAPHIFY_API_TIMEOUT", "").strip()
|
||||
timeout_s: float = 600.0
|
||||
if timeout_raw:
|
||||
try:
|
||||
v = float(timeout_raw)
|
||||
if v > 0:
|
||||
timeout_s = v
|
||||
except ValueError:
|
||||
pass
|
||||
client = OpenAI(api_key=api_key, base_url=base_url, timeout=timeout_s)
|
||||
client = OpenAI(api_key=api_key, base_url=base_url, timeout=_resolve_api_timeout())
|
||||
kwargs: dict = {
|
||||
"model": model,
|
||||
"messages": [
|
||||
@@ -550,7 +554,7 @@ def _call_claude(api_key: str, model: str, user_message: str, max_tokens: int =
|
||||
except ImportError as exc:
|
||||
raise ImportError(_backend_pkg_hint("anthropic", "anthropic")) from exc
|
||||
|
||||
client = anthropic.Anthropic(api_key=api_key)
|
||||
client = anthropic.Anthropic(api_key=api_key, timeout=_resolve_api_timeout())
|
||||
resp = client.messages.create(
|
||||
model=model,
|
||||
max_tokens=max_tokens,
|
||||
@@ -637,7 +641,7 @@ def _call_claude_cli(user_message: str, max_tokens: int = 8192, *, deep_mode: bo
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8", # Force UTF-8 — prevents UnicodeEncodeError on Windows cp1252
|
||||
timeout=600,
|
||||
timeout=_resolve_api_timeout(),
|
||||
check=False,
|
||||
)
|
||||
if proc.returncode != 0:
|
||||
@@ -1200,7 +1204,7 @@ def _call_llm(prompt: str, *, backend: str, max_tokens: int = 200) -> str:
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8", # Force UTF-8 — prevents UnicodeEncodeError on Windows cp1252
|
||||
timeout=600,
|
||||
timeout=_resolve_api_timeout(),
|
||||
check=False,
|
||||
)
|
||||
if proc.returncode != 0:
|
||||
|
||||
@@ -193,3 +193,38 @@ def test_non_windows_uses_bare_claude(monkeypatch):
|
||||
|
||||
argv = run.call_args.args[0]
|
||||
assert argv[0] == "claude"
|
||||
|
||||
|
||||
# ---------- GRAPHIFY_API_TIMEOUT honoured by all backends ----------
|
||||
|
||||
|
||||
def test_resolve_api_timeout_default(monkeypatch):
|
||||
monkeypatch.delenv("GRAPHIFY_API_TIMEOUT", raising=False)
|
||||
assert llm._resolve_api_timeout() == 600.0
|
||||
|
||||
|
||||
def test_resolve_api_timeout_env_override(monkeypatch):
|
||||
monkeypatch.setenv("GRAPHIFY_API_TIMEOUT", "45")
|
||||
assert llm._resolve_api_timeout() == 45.0
|
||||
|
||||
|
||||
def test_resolve_api_timeout_ignores_invalid(monkeypatch):
|
||||
monkeypatch.setenv("GRAPHIFY_API_TIMEOUT", "not-a-number")
|
||||
assert llm._resolve_api_timeout() == 600.0
|
||||
|
||||
|
||||
def test_resolve_api_timeout_ignores_nonpositive(monkeypatch):
|
||||
monkeypatch.setenv("GRAPHIFY_API_TIMEOUT", "0")
|
||||
assert llm._resolve_api_timeout() == 600.0
|
||||
|
||||
|
||||
def test_claude_cli_extraction_honours_timeout(monkeypatch, fake_claude):
|
||||
monkeypatch.setenv("GRAPHIFY_API_TIMEOUT", "30")
|
||||
llm._call_claude_cli("dummy", max_tokens=8192)
|
||||
assert fake_claude.call_args.kwargs["timeout"] == 30.0
|
||||
|
||||
|
||||
def test_call_llm_claude_cli_branch_honours_timeout(monkeypatch, fake_claude):
|
||||
monkeypatch.setenv("GRAPHIFY_API_TIMEOUT", "30")
|
||||
llm._call_llm(prompt="x", backend="claude-cli", max_tokens=10)
|
||||
assert fake_claude.call_args.kwargs["timeout"] == 30.0
|
||||
|
||||
Reference in New Issue
Block a user