Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,33 @@ The MCP server gives your assistant structured access: `query_graph`, `get_node`

---

## Using Kimi or a custom "cheaper" LLM instead of Claude

Graphify can use **Kimi 2.6** (Via Moonshot) or any other openai-compatible model for semantic extraction. Kimi, for example is roughly 3x cheaper than claude per token.

### Option A: Kimi 2.6 via Moonshot (recommended)

Get a key from https://platform.moonshot.ai/, configure it as an env var, No further config needed.

```bash
export MOONSHOT_API_KEY=<your-moonshot-key>
```

### Option B: Any other LLM provider

Graphify can use any LLM provider which gives an openAI compatible endpoint:

Example with Openrouter and Qwen3.6
```bash
export GRAPHIFY_LLM_BASE_URL=https://openrouter.ai/api/v1
export GRAPHIFY_LLM_MODEL=qwen/qwen3.6-35b-a3b
export GRAPHIFY_LLM_API_KEY=<your-openrouter-key>
# Optional, if required by your model
export GRAPHIFY_LLM_TEMPERATURE=0.6
```

---

## Full command reference

```
Expand Down
15 changes: 13 additions & 2 deletions graphify/llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ def _get_tokenizer():
"pricing": {"input": 0.74, "output": 4.66}, # USD per 1M tokens
"temperature": None, # kimi-k2.6 enforces its own fixed temperature; sending any value raises 400
},
"custom": {
# For users who want to specify their own OpenAI-compatible endpoint and model.
"base_url": os.environ.get("GRAPHIFY_LLM_BASE_URL", None), # Must be set by caller
"default_model": os.environ.get("GRAPHIFY_LLM_MODEL", "kimi-k2.5"), # Must be set by caller
"env_key": "GRAPHIFY_LLM_API_KEY",
"pricing": {"input": 0.0, "output": 0.0}, # User must estimate separately
"temperature": os.environ.get("GRAPHIFY_LLM_TEMPERATURE", None), # Optional override; defaults to None (use model default)
}
}

_EXTRACTION_SYSTEM = """\
Expand Down Expand Up @@ -468,11 +476,14 @@ def estimate_cost(backend: str, input_tokens: int, output_tokens: int) -> float:
def detect_backend() -> str | None:
"""Return the name of whichever backend has an API key set, or None.

Kimi is checked first (opt-in). Falls back to Claude if ANTHROPIC_API_KEY is set.
Claude is the default for the skill.md subagent pipeline and is never forced here.
Kimi is checked first (opt-in). Then checks for a custom defined LLM with Base URL.
Falls back to Claude if ANTHROPIC_API_KEY is set. Claude is the default for the skill.md
subagent pipeline and is never forced here.
"""
if os.environ.get("MOONSHOT_API_KEY"):
return "kimi"
if os.environ.get("GRAPHIFY_LLM_BASE_URL"):
return "custom"
if os.environ.get("ANTHROPIC_API_KEY"):
return "claude"
return None