Skip to content
Merged
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
28 changes: 24 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,16 +253,36 @@ read-only toolset yet (#88).

## Providers

lex-code can talk to eight provider backends (see `--help` for the full
flag list — Anthropic, OpenAI, Google, Mistral, LiteLLM, vLLM, and Vertex
are implemented in code alongside the two below), but only these two have
actually been run end-to-end against this repo:
lex-code can talk to ten provider backends (see `--help` for the full
flag list — Anthropic, OpenAI, Google, Mistral, LiteLLM, vLLM, lex-gpu and
Vertex are implemented in code alongside the two below), but only these two
have actually been run end-to-end against this repo:

| Flag | Provider | Model | Key required |
|------|----------|-------|--------------|
| `--ollama` | Ollama (local, native API) | `$OLLAMA_MODEL` (default `qwen3.8:27b-mlx`) | none |
| `--opencode` | OpenCode Go plan (cloud, direct) | `$OPENCODE_MODEL` | `OPENCODE_API_KEY` |

### lex-gpu

`--lex-gpu` points at [lex-gpu](https://github.com/alpibrusl/lex-gpu)'s
server, which answers OpenAI chat completions from its own compiled Metal
and CUDA kernels rather than llama.cpp or MLX. No key; `$LEX_GPU_BASE_URL`
overrides the default `http://127.0.0.1:8080`.

```sh
cargo run --release -p lex-rt --example serve -- --model qwen3.8:27b-mlx
lex-code --lex-gpu --explore "what does src/agents/build.lex do?"
```

**Chat only, for now.** lex-gpu accepts a `tools` list and drops it, so the
model is never told the tools exist and its replies carry no `tool_calls` —
the agent loop gets an answer and never dispatches a tool, which for a
coding agent means it will describe work rather than do it. The wiring is
here so that it starts working the moment lex-gpu renders `tools` into its
prompt and splits `<think>` out of `content`; neither needs a change on
this side.

### Ollama

_Runnable: `examples/providers/ollama.sh`_
Expand Down
5 changes: 5 additions & 0 deletions src/agents/bar.lex
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ fn vllm_agent() -> [env] ag.AgentLoop {
ag.with_permission_gate(base, rules.bar_permission())
}

fn lex_gpu_agent() -> [env] ag.AgentLoop {
let base := { name: "bar", goal: barp.system(), model: prov.make_model_ref("lex-gpu", providers.lex_gpu_model()), provider: providers.lex_gpu_local(), tools: tools.bar_tools(), options: { temperature: None, top_p: None, max_steps: Some(25), max_tokens: None }, permission_spec: None }
ag.with_permission_gate(base, rules.bar_permission())
}

fn opencode_agent() -> [env] ag.AgentLoop {
let base := { name: "bar", goal: barp.system(), model: prov.make_model_ref("opencode", tools.opencode_model()), provider: providers.opencode_go(), tools: tools.bar_tools(), options: { temperature: None, top_p: None, max_steps: Some(25), max_tokens: None }, permission_spec: None }
ag.with_permission_gate(base, rules.bar_permission())
Expand Down
5 changes: 5 additions & 0 deletions src/agents/build.lex
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ fn vllm_agent() -> [env] ag.AgentLoop {
ag.with_permission_gate(base, rules.build_permission())
}

fn lex_gpu_agent() -> [env] ag.AgentLoop {
let base := { name: "build", goal: bp.system(), model: prov.make_model_ref("lex-gpu", providers.lex_gpu_model()), provider: providers.lex_gpu_local(), tools: tools.all_tools(), options: { temperature: None, top_p: None, max_steps: Some(50), max_tokens: None }, permission_spec: None }
ag.with_permission_gate(base, rules.build_permission())
}

fn google_agent() -> [env] ag.AgentLoop {
let base := { name: "build", goal: bp.system(), model: prov.gemini_pro(), provider: providers.google(), tools: tools.all_tools(), options: { temperature: None, top_p: None, max_steps: Some(50), max_tokens: None }, permission_spec: None }
ag.with_permission_gate(base, rules.build_permission())
Expand Down
5 changes: 5 additions & 0 deletions src/agents/explore.lex
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ fn vllm_agent() -> [env] ag.AgentLoop {
ag.with_permission_gate(base, rules.explore_permission())
}

fn lex_gpu_agent() -> [env] ag.AgentLoop {
let base := { name: "explore", goal: ep.system(), model: prov.make_model_ref("lex-gpu", providers.lex_gpu_model()), provider: providers.lex_gpu_local(), tools: tools.tools_for_spec(rules.explore_permission()), options: { temperature: None, top_p: None, max_steps: Some(20), max_tokens: None }, permission_spec: None }
ag.with_permission_gate(base, rules.explore_permission())
}

fn openai_agent() -> [env] ag.AgentLoop {
let base := { name: "explore", goal: ep.system(), model: prov.gpt4o_mini(), provider: providers.openai(), tools: tools.tools_for_spec(rules.explore_permission()), options: { temperature: None, top_p: None, max_steps: Some(20), max_tokens: None }, permission_spec: None }
ag.with_permission_gate(base, rules.explore_permission())
Expand Down
5 changes: 5 additions & 0 deletions src/agents/plan.lex
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ fn vllm_agent() -> [env] ag.AgentLoop {
ag.with_permission_gate(base, rules.plan_permission())
}

fn lex_gpu_agent() -> [env] ag.AgentLoop {
let base := { name: "plan", goal: pp.system(), model: prov.make_model_ref("lex-gpu", providers.lex_gpu_model()), provider: providers.lex_gpu_local(), tools: tools.tools_for_spec(rules.plan_permission()), options: { temperature: None, top_p: None, max_steps: Some(30), max_tokens: None }, permission_spec: None }
ag.with_permission_gate(base, rules.plan_permission())
}

fn openai_agent() -> [env] ag.AgentLoop {
let base := { name: "plan", goal: pp.system(), model: prov.gpt4o(), provider: providers.openai(), tools: tools.tools_for_spec(rules.plan_permission()), options: { temperature: None, top_p: None, max_steps: Some(30), max_tokens: None }, permission_spec: None }
ag.with_permission_gate(base, rules.plan_permission())
Expand Down
5 changes: 5 additions & 0 deletions src/agents/refactor.lex
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ fn vllm_agent() -> [env] ag.AgentLoop {
ag.with_permission_gate(base, rules.refactor_permission())
}

fn lex_gpu_agent() -> [env] ag.AgentLoop {
let base := { name: "refactor", goal: rp.system(), model: prov.make_model_ref("lex-gpu", providers.lex_gpu_model()), provider: providers.lex_gpu_local(), tools: tools.tools_for_spec(rules.refactor_permission()), options: { temperature: None, top_p: None, max_steps: Some(40), max_tokens: None }, permission_spec: None }
ag.with_permission_gate(base, rules.refactor_permission())
}

fn openai_agent() -> [env] ag.AgentLoop {
let base := { name: "refactor", goal: rp.system(), model: prov.gpt4o(), provider: providers.openai(), tools: tools.tools_for_spec(rules.refactor_permission()), options: { temperature: None, top_p: None, max_steps: Some(40), max_tokens: None }, permission_spec: None }
ag.with_permission_gate(base, rules.refactor_permission())
Expand Down
5 changes: 5 additions & 0 deletions src/agents/review.lex
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ fn vllm_agent() -> [env] ag.AgentLoop {
ag.with_permission_gate(base, rules.review_permission())
}

fn lex_gpu_agent() -> [env] ag.AgentLoop {
let base := { name: "review", goal: rvp.system(), model: prov.make_model_ref("lex-gpu", providers.lex_gpu_model()), provider: providers.lex_gpu_local(), tools: tools.tools_for_spec(rules.review_permission()), options: { temperature: None, top_p: None, max_steps: Some(25), max_tokens: None }, permission_spec: None }
ag.with_permission_gate(base, rules.review_permission())
}

fn openai_agent() -> [env] ag.AgentLoop {
let base := { name: "review", goal: rvp.system(), model: prov.gpt4o(), provider: providers.openai(), tools: tools.tools_for_spec(rules.review_permission()), options: { temperature: None, top_p: None, max_steps: Some(25), max_tokens: None }, permission_spec: None }
ag.with_permission_gate(base, rules.review_permission())
Expand Down
5 changes: 5 additions & 0 deletions src/agents/spec_agent.lex
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ fn vllm_agent() -> [env] ag.AgentLoop {
ag.with_permission_gate(base, rules.spec_permission())
}

fn lex_gpu_agent() -> [env] ag.AgentLoop {
let base := { name: "spec", goal: sp.system(), model: prov.make_model_ref("lex-gpu", providers.lex_gpu_model()), provider: providers.lex_gpu_local(), tools: tools.tools_for_spec(rules.spec_permission()), options: { temperature: None, top_p: None, max_steps: Some(30), max_tokens: None }, permission_spec: None }
ag.with_permission_gate(base, rules.spec_permission())
}

fn openai_agent() -> [env] ag.AgentLoop {
let base := { name: "spec", goal: sp.system(), model: prov.gpt4o(), provider: providers.openai(), tools: tools.tools_for_spec(rules.spec_permission()), options: { temperature: None, top_p: None, max_steps: Some(30), max_tokens: None }, permission_spec: None }
ag.with_permission_gate(base, rules.spec_permission())
Expand Down
5 changes: 5 additions & 0 deletions src/agents/test_agent.lex
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ fn vllm_agent() -> [env] ag.AgentLoop {
ag.with_permission_gate(base, rules.test_permission())
}

fn lex_gpu_agent() -> [env] ag.AgentLoop {
let base := { name: "test", goal: tp.system(), model: prov.make_model_ref("lex-gpu", providers.lex_gpu_model()), provider: providers.lex_gpu_local(), tools: tools.tools_for_spec(rules.test_permission()), options: { temperature: None, top_p: None, max_steps: Some(30), max_tokens: None }, permission_spec: None }
ag.with_permission_gate(base, rules.test_permission())
}

fn openai_agent() -> [env] ag.AgentLoop {
let base := { name: "test", goal: tp.system(), model: prov.gpt4o_mini(), provider: providers.openai(), tools: tools.tools_for_spec(rules.test_permission()), options: { temperature: None, top_p: None, max_steps: Some(30), max_tokens: None }, permission_spec: None }
ag.with_permission_gate(base, rules.test_permission())
Expand Down
5 changes: 5 additions & 0 deletions src/agents/verify.lex
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ fn vllm_agent() -> [env] ag.AgentLoop {
ag.with_permission_gate(base, rules.verify_permission())
}

fn lex_gpu_agent() -> [env] ag.AgentLoop {
let base := { name: "verify", goal: vp.system(), model: prov.make_model_ref("lex-gpu", providers.lex_gpu_model()), provider: providers.lex_gpu_local(), tools: tools.tools_for_spec(rules.verify_permission()), options: { temperature: None, top_p: None, max_steps: Some(25), max_tokens: None }, permission_spec: None }
ag.with_permission_gate(base, rules.verify_permission())
}

fn openai_agent() -> [env] ag.AgentLoop {
let base := { name: "verify", goal: vp.system(), model: prov.gpt4o(), provider: providers.openai(), tools: tools.tools_for_spec(rules.verify_permission()), options: { temperature: None, top_p: None, max_steps: Some(25), max_tokens: None }, permission_spec: None }
ag.with_permission_gate(base, rules.verify_permission())
Expand Down
11 changes: 11 additions & 0 deletions src/server/session.lex
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,17 @@ fn pick_agent(mode :: AgentMode, provider_tag :: Str) -> [env] ag.AgentLoop {
Bar => bar_a.vllm_agent(),
Verify => verify_a.vllm_agent(),
},
"lex-gpu" => match mode {
Build => build_agent.lex_gpu_agent(),
Plan => plan_agent.lex_gpu_agent(),
Explore => explore_agent.lex_gpu_agent(),
Refactor => refactor_agent.lex_gpu_agent(),
Spec => spec_a.lex_gpu_agent(),
Test => test_a.lex_gpu_agent(),
Review => review_a.lex_gpu_agent(),
Bar => bar_a.lex_gpu_agent(),
Verify => verify_a.lex_gpu_agent(),
},
"vertex" => match mode {
Build => build_agent.vertex_agent(),
Plan => plan_agent.google_agent(),
Expand Down
12 changes: 8 additions & 4 deletions src/tui/main.lex
Original file line number Diff line number Diff line change
Expand Up @@ -538,10 +538,14 @@ fn select_provider_tag(argv :: List[Str]) -> Str
if has_flag(argv, "--vllm") {
"vllm"
} else {
if has_flag(argv, "--opencode") {
"opencode"
if has_flag(argv, "--lex-gpu") {
"lex-gpu"
} else {
"litellm"
if has_flag(argv, "--opencode") {
"opencode"
} else {
"litellm"
}
}
}
}
Expand Down Expand Up @@ -799,7 +803,7 @@ fn dispatch(inv :: Invocation, mode :: sess.AgentMode, provider_tag :: Str) -> [
io.print(str.concat("lex-code — Lex-specialized coding assistant", "\n"))
io.print(str.concat("modes: --plan | --explore | --refactor | --spec | --test | --review | --verify | --bar | --multi", "\n"))
io.print(str.join(["pipelines: --pipeline=", str.join(graph.preset_names(), " | --pipeline="), "\n --pipeline=build,spec,test|review (\",\" in order, \"|\" at once)", "\n"], ""))
io.print(str.concat("providers: --mistral | --openai | --google | --vertex | --litellm | --ollama | --vllm | --opencode (default: anthropic)", "\n"))
io.print(str.concat("providers: --mistral | --openai | --google | --vertex | --litellm | --ollama | --vllm | --lex-gpu | --opencode (default: anthropic)", "\n"))
io.print(str.concat("one-shot: lex run src/tui/main.lex -- [flags] \"your task\"", "\n"))
io.print(str.concat("issue: --issue=<id> [\"extra guidance\"] implement a typed issue from its acceptance, then verify it", "\n"))
io.print(str.concat("refine: --refine=<id> propose a typed acceptance for a free_form issue (you approve it)", "\n"))
Expand Down
Loading