← back to Exo
fix ollama API compatibility for VS Code Copilot (#2091)
e8ec8d501004b5152d098109bd3b3f3529a73f34 · 2026-05-14 17:12:58 +0100 · Heidar
Ollama adapter fixes for VS Code Copilot (#2042):
- /api/version: bare semver "1.0.0" - Copilot parseInts each segment.
- /api/show: populate model_info + capabilities - Copilot crashes on
null model_info and filters by `tools`.
- Add POST /ollama/v1/chat/completions - ollama serves the OpenAI-compat
route here, BYOK clients 405 without it.
Before:
<img width="1380" height="144" alt="image"
src="https://github.com/user-attachments/assets/99d5464f-187d-4432-9a31-8229c55aa209"
/>
After:
<img width="1362" height="181" alt="image"
src="https://github.com/user-attachments/assets/361dc006-d8df-435f-8d8b-4fa4f44a8c23"
/>
<img width="279" height="909" alt="image"
src="https://github.com/user-attachments/assets/4621aba7-bd57-4762-8568-34a3383a6025"
/>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Files touched
M src/exo/api/main.pyM src/exo/api/types/ollama_api.py
Diff
commit e8ec8d501004b5152d098109bd3b3f3529a73f34
Author: Heidar <74025356+Heidar-An@users.noreply.github.com>
Date: Thu May 14 17:12:58 2026 +0100
fix ollama API compatibility for VS Code Copilot (#2091)
Ollama adapter fixes for VS Code Copilot (#2042):
- /api/version: bare semver "1.0.0" - Copilot parseInts each segment.
- /api/show: populate model_info + capabilities - Copilot crashes on
null model_info and filters by `tools`.
- Add POST /ollama/v1/chat/completions - ollama serves the OpenAI-compat
route here, BYOK clients 405 without it.
Before:
<img width="1380" height="144" alt="image"
src="https://github.com/user-attachments/assets/99d5464f-187d-4432-9a31-8229c55aa209"
/>
After:
<img width="1362" height="181" alt="image"
src="https://github.com/user-attachments/assets/361dc006-d8df-435f-8d8b-4fa4f44a8c23"
/>
<img width="279" height="909" alt="image"
src="https://github.com/user-attachments/assets/4621aba7-bd57-4762-8568-34a3383a6025"
/>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
---
src/exo/api/main.py | 23 ++++++++++++++++++++++-
src/exo/api/types/ollama_api.py | 11 +++++++++++
2 files changed, 33 insertions(+), 1 deletion(-)
diff --git a/src/exo/api/main.py b/src/exo/api/main.py
index e346a4f9..d3b03e46 100644
--- a/src/exo/api/main.py
+++ b/src/exo/api/main.py
@@ -104,6 +104,7 @@ from exo.api.types.claude_api import (
ClaudeMessagesResponse,
)
from exo.api.types.ollama_api import (
+ OllamaCapability,
OllamaChatRequest,
OllamaChatResponse,
OllamaGenerateRequest,
@@ -138,6 +139,7 @@ from exo.shared.models import model_cards
from exo.shared.models.model_cards import (
ModelCard,
ModelId,
+ ModelTask,
)
from exo.shared.tracing import TraceEvent, compute_stats, export_trace, load_trace_file
from exo.shared.types.chunks import (
@@ -375,6 +377,9 @@ class API:
# Ollama API
self.app.head("/ollama/")(self.ollama_version)
self.app.head("/ollama/api/version")(self.ollama_version)
+ self.app.post("/ollama/v1/chat/completions", response_model=None)(
+ self.chat_completions
+ )
self.app.post("/ollama/api/chat", response_model=None)(self.ollama_chat)
self.app.post("/ollama/api/api/chat", response_model=None)(self.ollama_chat)
self.app.post("/ollama/api/v1/chat", response_model=None)(self.ollama_chat)
@@ -1678,6 +1683,20 @@ class API:
status_code=404, detail=f"Model not found: {model_name}"
) from exc
+ capabilities: list[OllamaCapability] = []
+ if ModelTask.TextGeneration in card.tasks:
+ capabilities.extend(("completion", "tools"))
+ if card.vision is not None:
+ capabilities.append("vision")
+
+ architecture = card.family or "unknown"
+ model_info: dict[str, Any] = {
+ "general.architecture": architecture,
+ "general.basename": card.base_model or str(card.model_id),
+ }
+ if card.context_length > 0:
+ model_info[f"{architecture}.context_length"] = card.context_length
+
return OllamaShowResponse(
modelfile=f"FROM {card.model_id}",
template="{{ .Prompt }}",
@@ -1685,6 +1704,8 @@ class API:
family=card.family or None,
quantization_level=card.quantization or None,
),
+ model_info=model_info,
+ capabilities=capabilities,
)
async def ollama_ps(self) -> OllamaPsResponse:
@@ -1707,7 +1728,7 @@ class API:
async def ollama_version(self) -> dict[str, str]:
"""Returns version information for Ollama API compatibility."""
- return {"version": "exo v1.0"}
+ return {"version": "1.0.0"}
def _calculate_total_available_memory(self) -> Memory:
"""Calculate total available memory across all nodes in bytes."""
diff --git a/src/exo/api/types/ollama_api.py b/src/exo/api/types/ollama_api.py
index 58a54ac0..c122a5dc 100644
--- a/src/exo/api/types/ollama_api.py
+++ b/src/exo/api/types/ollama_api.py
@@ -11,6 +11,16 @@ from exo.shared.models.model_cards import ModelId
OllamaRole = Literal["system", "user", "assistant", "tool"]
OllamaDoneReason = Literal["stop", "length", "tool_call", "error"]
+OllamaCapability = Literal[
+ "completion",
+ "tools",
+ "insert",
+ "vision",
+ "embedding",
+ "thinking",
+ "image",
+ "audio",
+]
class OllamaToolFunction(BaseModel, frozen=True):
@@ -133,6 +143,7 @@ class OllamaShowResponse(BaseModel, frozen=True, strict=True):
template: str | None = None
details: OllamaModelDetails | None = None
model_info: dict[str, Any] | None = None
+ capabilities: list[OllamaCapability] = []
class OllamaPsModel(BaseModel, frozen=True, strict=True):
← 1fd15d59 create directory on startup (#2089)
·
back to Exo
·
fix: omit null delta fields in streaming chat completions (i 88d46d46 →