kivi-cli 2.0.6__tar.gz → 2.0.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {kivi_cli-2.0.6/src/kivi_cli.egg-info → kivi_cli-2.0.7}/PKG-INFO +1 -1
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/pyproject.toml +1 -1
- {kivi_cli-2.0.6 → kivi_cli-2.0.7/src/kivi_cli.egg-info}/PKG-INFO +1 -1
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kivi_cli.egg-info/SOURCES.txt +1 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/backend.py +10 -2
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/index.html +37 -2
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/providers/base.py +1 -1
- kivi_cli-2.0.7/src/kiviai/services/providers/gemini.py +120 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/providers/model_costs.py +14 -1
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/providers/opencode.py +1 -1
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/providers/registry.py +6 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/MANIFEST.in +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/setup.cfg +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kivi_cli.egg-info/dependency_links.txt +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kivi_cli.egg-info/entry_points.txt +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kivi_cli.egg-info/requires.txt +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kivi_cli.egg-info/top_level.txt +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/__init__.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/__init__.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/git_service.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/monitor.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/providers/__init__.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/providers/claude.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/providers/copilot.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/providers/inhouse.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/providers/kivi.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/providers/openai_agent.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/providers/orchestrator_provider.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/providers/vllm.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/scheduler.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/__init__.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/__init__.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/agent_orchestration/__init__.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/agent_orchestration/tools.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/bash_execution/__init__.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/bash_execution/sessions.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/code_search/__init__.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/code_search/tools.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/file_operations/__init__.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/file_operations/tools.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/markdown/__init__.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/markdown/custom_markdownify.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/markdown/mrkdwn_analysis.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/markdown/tools.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/session_workflow/__init__.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/session_workflow/tools.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/web/__init__.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/web/_fetcher.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/web/_store.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/web/fetch.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/web/search.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/web/store_tools.py +0 -0
- {kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/tool_manager.py +0 -0
|
@@ -17,6 +17,7 @@ src/kiviai/services/providers/__init__.py
|
|
|
17
17
|
src/kiviai/services/providers/base.py
|
|
18
18
|
src/kiviai/services/providers/claude.py
|
|
19
19
|
src/kiviai/services/providers/copilot.py
|
|
20
|
+
src/kiviai/services/providers/gemini.py
|
|
20
21
|
src/kiviai/services/providers/inhouse.py
|
|
21
22
|
src/kiviai/services/providers/kivi.py
|
|
22
23
|
src/kiviai/services/providers/model_costs.py
|
|
@@ -33,7 +33,7 @@ from kiviai.services.git_service import GitService
|
|
|
33
33
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
|
34
34
|
log = logging.getLogger("kiviai")
|
|
35
35
|
|
|
36
|
-
app = FastAPI(title="Kivi AI v2", version="2.0.
|
|
36
|
+
app = FastAPI(title="Kivi AI v2", version="2.0.7")
|
|
37
37
|
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])
|
|
38
38
|
|
|
39
39
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
@@ -161,7 +161,7 @@ async def _run_task(task: dict):
|
|
|
161
161
|
"opencode": "opencode",
|
|
162
162
|
"copilot": "copilot",
|
|
163
163
|
"local": "vllm",
|
|
164
|
-
|
|
164
|
+
"gemini": "gemini",
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
# Map short model names to current Claude model IDs.
|
|
@@ -189,6 +189,13 @@ async def _run_task(task: dict):
|
|
|
189
189
|
if "vllm_model" not in extra:
|
|
190
190
|
extra["vllm_model"] = ""
|
|
191
191
|
|
|
192
|
+
# For "gemini" agent, pass gemini model from extra
|
|
193
|
+
if model_name == "gemini":
|
|
194
|
+
extra = task.setdefault("extra", {})
|
|
195
|
+
if extra.get("gemini_model"):
|
|
196
|
+
extra["model_override"] = extra["gemini_model"]
|
|
197
|
+
if extra.get("gemini_api_key"):
|
|
198
|
+
extra["api_key"] = extra["gemini_api_key"]
|
|
192
199
|
|
|
193
200
|
log.info("_run_task starting: agent=%s provider=%s prompt=%s", model_name, provider_name, task["prompt"][:60])
|
|
194
201
|
try:
|
|
@@ -1254,6 +1261,7 @@ _AGENTS = [
|
|
|
1254
1261
|
{"id": "opencode", "name": "OpenCode", "provider": "opencode", "description": "Open-source agentic coding CLI"},
|
|
1255
1262
|
{"id": "copilot", "name": "Copilot", "provider": "copilot", "description": "GitHub Copilot SDK agent"},
|
|
1256
1263
|
{"id": "local", "name": "Local", "provider": "vllm", "description": "Local vLLM model (Qwen 3.5-9B)"},
|
|
1264
|
+
{"id": "gemini", "name": "Gemini", "provider": "gemini", "description": "Google Gemini — fast streaming, great for brainstorming"},
|
|
1257
1265
|
]
|
|
1258
1266
|
|
|
1259
1267
|
|
|
@@ -3190,6 +3190,7 @@ textarea:focus-visible {
|
|
|
3190
3190
|
<option value="claude-code">Claude Code</option>
|
|
3191
3191
|
<option value="opencode">OpenCode</option>
|
|
3192
3192
|
<option value="copilot">Copilot</option>
|
|
3193
|
+
<option value="gemini">Gemini</option>
|
|
3193
3194
|
</select>
|
|
3194
3195
|
<select id="ag-model"></select>
|
|
3195
3196
|
<input class="ag-cwd" id="ag-cwd" placeholder="~/project" autocomplete="off" />
|
|
@@ -3227,6 +3228,7 @@ textarea:focus-visible {
|
|
|
3227
3228
|
<option value="claude-code">Claude Code</option>
|
|
3228
3229
|
<option value="opencode">OpenCode</option>
|
|
3229
3230
|
<option value="copilot">Copilot</option>
|
|
3231
|
+
<option value="gemini">Gemini</option>
|
|
3230
3232
|
</select>
|
|
3231
3233
|
<div class="ctrl-sep"></div>
|
|
3232
3234
|
<div class="ctrl-group">
|
|
@@ -3494,20 +3496,40 @@ textarea:focus-visible {
|
|
|
3494
3496
|
</table>
|
|
3495
3497
|
</div>
|
|
3496
3498
|
|
|
3499
|
+
<h3 class="docs-h3">Gemini CLI flags — supported via UI</h3>
|
|
3500
|
+
<div class="docs-table-wrap">
|
|
3501
|
+
<table class="docs-table">
|
|
3502
|
+
<thead><tr><th>UI Control</th><th>Equivalent CLI flag</th><th>Notes</th></tr></thead>
|
|
3503
|
+
<tbody>
|
|
3504
|
+
<tr><td>Agent dropdown → Gemini (planned)</td><td><code>gemini -p "…"</code></td><td>Provider not wired yet — see roadmap</td></tr>
|
|
3505
|
+
<tr><td>Model picker</td><td><code>-m gemini-2.5-pro</code></td><td>Any Gemini model string</td></tr>
|
|
3506
|
+
<tr><td>Mode → plan</td><td><code>--approval-mode plan</code></td><td>Read-only, no writes</td></tr>
|
|
3507
|
+
<tr><td>Mode → auto</td><td><code>--approval-mode auto_edit</code></td><td>Auto-approve edits, prompt for rest</td></tr>
|
|
3508
|
+
<tr><td>Mode → bypass</td><td><code>--approval-mode yolo</code></td><td>Auto-approve everything</td></tr>
|
|
3509
|
+
<tr><td>Resume session</td><td><code>-r latest</code></td><td>Gemini uses index-based resume</td></tr>
|
|
3510
|
+
<tr><td>CWD / extra dirs</td><td><code>--include-directories</code></td><td>Comma-separated extra workspace dirs</td></tr>
|
|
3511
|
+
<tr><td>Sandbox toggle (planned)</td><td><code>-s --sandbox</code></td><td>Isolated execution environment</td></tr>
|
|
3512
|
+
</tbody>
|
|
3513
|
+
</table>
|
|
3514
|
+
</div>
|
|
3497
3515
|
|
|
3498
3516
|
<h3 class="docs-h3">Feature Roadmap — CLI parity gaps to close</h3>
|
|
3499
3517
|
<div class="docs-table-wrap">
|
|
3500
3518
|
<table class="docs-table">
|
|
3501
3519
|
<thead><tr><th>Feature</th><th>CLI Source</th><th>Priority</th><th>Status</th></tr></thead>
|
|
3502
3520
|
<tbody>
|
|
3521
|
+
<tr><td>Gemini provider wiring</td><td>Gemini CLI <code>-p</code> headless</td><td>High</td><td class="status-done">Done</td></tr>
|
|
3522
|
+
<tr><td>Session fork UI</td><td>OpenCode <code>--fork</code>, Claude <code>--fork-session</code></td><td>High</td><td class="status-done">Done</td></tr>
|
|
3503
3523
|
<tr><td>Thinking blocks toggle</td><td>OpenCode <code>--thinking</code></td><td>Medium</td><td class="status-done">Done</td></tr>
|
|
3504
3524
|
<tr><td>Effort level selector</td><td>Claude <code>--effort low/medium/high/xhigh/max</code></td><td>Medium</td><td class="status-done">Done</td></tr>
|
|
3505
3525
|
<tr><td>Max budget USD input</td><td>Claude <code>--max-budget-usd</code></td><td>Medium</td><td class="status-done">Done</td></tr>
|
|
3506
3526
|
<tr><td>Allowed/disallowed tools</td><td>Claude <code>--allowedTools / --disallowedTools</code></td><td>Medium</td><td class="status-done">Done</td></tr>
|
|
3507
3527
|
<tr><td>Custom system prompt</td><td>Claude <code>--system-prompt / --append-system-prompt</code></td><td>High</td><td class="status-done">Done</td></tr>
|
|
3508
|
-
<tr><td>MCP server config panel</td><td>Claude <code>--mcp-config</code></td><td>High</td><td class="status-todo">TODO</td></tr>
|
|
3528
|
+
<tr><td>MCP server config panel</td><td>Claude/Gemini <code>--mcp-config</code></td><td>High</td><td class="status-todo">TODO</td></tr>
|
|
3509
3529
|
<tr><td>File attachments on prompt</td><td>OpenCode <code>-f --file</code>, Claude <code>--file</code></td><td>High</td><td class="status-done">Done</td></tr>
|
|
3510
|
-
<tr><td>Worktree per task</td><td>Claude <code>-w --worktree</code></td><td>Medium</td><td class="status-todo">TODO</td></tr>
|
|
3530
|
+
<tr><td>Worktree per task</td><td>Claude/Gemini <code>-w --worktree</code></td><td>Medium</td><td class="status-todo">TODO</td></tr>
|
|
3531
|
+
<tr><td>Sandbox toggle</td><td>Gemini <code>-s --sandbox</code></td><td>Low</td><td class="status-todo">TODO</td></tr>
|
|
3532
|
+
<tr><td>Session export/import</td><td>OpenCode <code>export / import</code></td><td>Medium</td><td class="status-todo">TODO</td></tr>
|
|
3511
3533
|
<tr><td>OpenCode stats panel</td><td>OpenCode <code>stats</code> (token usage/cost history)</td><td>Low</td><td class="status-todo">TODO</td></tr>
|
|
3512
3534
|
<tr><td>PR auto-run (opencode pr)</td><td>OpenCode <code>pr <number></code></td><td>Medium</td><td class="status-todo">TODO</td></tr>
|
|
3513
3535
|
<tr><td>Output format (JSON/stream)</td><td>Claude <code>--output-format stream-json</code></td><td>Low</td><td class="status-done">Done (internal)</td></tr>
|
|
@@ -5080,6 +5102,7 @@ const AGENT_MODELS = {
|
|
|
5080
5102
|
{ v: 'anthropic/claude-sonnet-5', label: 'anthropic/claude-sonnet-5' },
|
|
5081
5103
|
{ v: 'anthropic/claude-opus-4-8', label: 'anthropic/claude-opus-4-8' },
|
|
5082
5104
|
{ v: 'openai/gpt-5.4', label: 'openai/gpt-5.4' },
|
|
5105
|
+
{ v: 'google/gemini-2.5-pro', label: 'google/gemini-2.5-pro' },
|
|
5083
5106
|
],
|
|
5084
5107
|
'copilot': [
|
|
5085
5108
|
{ v: 'gpt-5.4', label: 'gpt-5.4' },
|
|
@@ -5094,6 +5117,14 @@ const AGENT_MODELS = {
|
|
|
5094
5117
|
{ v: 'o3-mini', label: 'o3-mini' },
|
|
5095
5118
|
{ v: 'o4-mini', label: 'o4-mini' },
|
|
5096
5119
|
],
|
|
5120
|
+
'gemini': [
|
|
5121
|
+
{ v: 'gemini-2.5-pro', label: 'gemini-2.5-pro' },
|
|
5122
|
+
{ v: 'gemini-2.5-flash', label: 'gemini-2.5-flash' },
|
|
5123
|
+
{ v: 'gemini-2.0-flash', label: 'gemini-2.0-flash' },
|
|
5124
|
+
{ v: 'gemini-2.0-flash-lite', label: 'gemini-2.0-flash-lite' },
|
|
5125
|
+
{ v: 'gemini-1.5-pro', label: 'gemini-1.5-pro' },
|
|
5126
|
+
{ v: 'gemini-1.5-flash', label: 'gemini-1.5-flash' },
|
|
5127
|
+
],
|
|
5097
5128
|
};
|
|
5098
5129
|
|
|
5099
5130
|
function renderModelOptions(models) {
|
|
@@ -5210,6 +5241,10 @@ function buildLaunchBody({ prompt, agent, selectedModel, cwd, extras }) {
|
|
|
5210
5241
|
body.extra.vllm_model = selectedModel || '';
|
|
5211
5242
|
body.extra.vllm_key = localStorage.getItem('vllm_key') || 'dummy';
|
|
5212
5243
|
}
|
|
5244
|
+
if (agent === 'gemini') {
|
|
5245
|
+
const gKey = localStorage.getItem('gemini_api_key');
|
|
5246
|
+
if (gKey) body.extra.api_key = gKey;
|
|
5247
|
+
if (selectedModel) body.extra.gemini_model = selectedModel;
|
|
5213
5248
|
}
|
|
5214
5249
|
return body;
|
|
5215
5250
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Base provider protocol for all AI integrations.
|
|
3
3
|
|
|
4
4
|
Every provider implements this interface so the system can swap
|
|
5
|
-
between Claude, Copilot, VLLM, etc. with zero friction.
|
|
5
|
+
between Claude, Gemini, Copilot, VLLM, etc. with zero friction.
|
|
6
6
|
"""
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""Google Gemini provider — uses google-genai SDK."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import asyncio
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
from typing import AsyncIterator
|
|
8
|
+
|
|
9
|
+
from .base import BaseProvider, ProviderConfig, ProviderEvent, EventType
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class GeminiProvider(BaseProvider):
|
|
13
|
+
name = "gemini"
|
|
14
|
+
display_name = "Google Gemini"
|
|
15
|
+
description = "Google's multimodal AI. Fast streaming text generation via the Gemini API. Best for quick questions, summaries, and brainstorming. Requires GEMINI_API_KEY."
|
|
16
|
+
supports_streaming = True
|
|
17
|
+
supports_tools = False
|
|
18
|
+
supports_sessions = False
|
|
19
|
+
available_models = [
|
|
20
|
+
"gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.0-flash",
|
|
21
|
+
"gemini-2.0-flash-lite", "gemini-1.5-pro", "gemini-1.5-flash",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
def __init__(self):
|
|
25
|
+
self._stop = False
|
|
26
|
+
|
|
27
|
+
async def run(self, prompt: str, config: ProviderConfig, history=None) -> AsyncIterator[ProviderEvent]:
|
|
28
|
+
self._stop = False
|
|
29
|
+
|
|
30
|
+
api_key = config.api_key or os.environ.get("GEMINI_API_KEY") or os.environ.get("GOOGLE_API_KEY", "")
|
|
31
|
+
model = config.model or "gemini-2.5-flash"
|
|
32
|
+
|
|
33
|
+
if not api_key:
|
|
34
|
+
yield ProviderEvent(type=EventType.ERROR, content="GEMINI_API_KEY not set")
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
from google import genai
|
|
39
|
+
from google.genai import types
|
|
40
|
+
|
|
41
|
+
client = genai.Client(api_key=api_key)
|
|
42
|
+
|
|
43
|
+
contents = []
|
|
44
|
+
if history:
|
|
45
|
+
for h in history:
|
|
46
|
+
if h.get("role") in ("user", "assistant") and not h.get("metadata"):
|
|
47
|
+
role = "user" if h["role"] == "user" else "model"
|
|
48
|
+
contents.append(types.Content(role=role, parts=[types.Part(text=h["content"])]))
|
|
49
|
+
contents.append(types.Content(role="user", parts=[types.Part(text=prompt)]))
|
|
50
|
+
|
|
51
|
+
response = await asyncio.to_thread(
|
|
52
|
+
lambda: client.models.generate_content_stream(
|
|
53
|
+
model=model,
|
|
54
|
+
contents=contents,
|
|
55
|
+
config=types.GenerateContentConfig(
|
|
56
|
+
temperature=0.7,
|
|
57
|
+
max_output_tokens=4096,
|
|
58
|
+
),
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
text_buf = ""
|
|
63
|
+
total_input_tokens = 0
|
|
64
|
+
total_output_tokens = 0
|
|
65
|
+
for chunk in response:
|
|
66
|
+
if self._stop:
|
|
67
|
+
yield ProviderEvent(type=EventType.TEXT, content="⏹ *stopped*")
|
|
68
|
+
yield ProviderEvent(type=EventType.DONE)
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
if chunk.text:
|
|
72
|
+
text_buf += chunk.text
|
|
73
|
+
yield ProviderEvent(type=EventType.TEXT, content=chunk.text)
|
|
74
|
+
|
|
75
|
+
# Capture usage metadata (available in the final chunk or via usage_metadata)
|
|
76
|
+
if hasattr(chunk, 'usage_metadata') and chunk.usage_metadata:
|
|
77
|
+
um = chunk.usage_metadata
|
|
78
|
+
total_input_tokens += getattr(um, 'prompt_token_count', 0) or 0
|
|
79
|
+
total_output_tokens += getattr(um, 'candidates_token_count', 0) or 0
|
|
80
|
+
|
|
81
|
+
except ImportError:
|
|
82
|
+
yield ProviderEvent(
|
|
83
|
+
type=EventType.ERROR,
|
|
84
|
+
content="google-genai package required: pip install google-genai",
|
|
85
|
+
)
|
|
86
|
+
return
|
|
87
|
+
except Exception as e:
|
|
88
|
+
yield ProviderEvent(type=EventType.ERROR, content=str(e))
|
|
89
|
+
return
|
|
90
|
+
|
|
91
|
+
# Emit cost event with calculated cost from usage
|
|
92
|
+
from .model_costs import estimate_cost
|
|
93
|
+
usage = {
|
|
94
|
+
"input_tokens": total_input_tokens,
|
|
95
|
+
"output_tokens": total_output_tokens,
|
|
96
|
+
}
|
|
97
|
+
total_cost = estimate_cost(
|
|
98
|
+
model,
|
|
99
|
+
input_tokens=total_input_tokens,
|
|
100
|
+
output_tokens=total_output_tokens,
|
|
101
|
+
)
|
|
102
|
+
yield ProviderEvent(
|
|
103
|
+
type=EventType.COST,
|
|
104
|
+
metadata={
|
|
105
|
+
"total_cost_usd": total_cost,
|
|
106
|
+
"usage": usage,
|
|
107
|
+
},
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
yield ProviderEvent(type=EventType.DONE)
|
|
111
|
+
|
|
112
|
+
async def stop(self):
|
|
113
|
+
self._stop = True
|
|
114
|
+
|
|
115
|
+
async def health_check(self):
|
|
116
|
+
api_key = os.environ.get("GEMINI_API_KEY") or os.environ.get("GOOGLE_API_KEY", "")
|
|
117
|
+
return {
|
|
118
|
+
"status": "ok" if api_key else "no_api_key",
|
|
119
|
+
"provider": self.name,
|
|
120
|
+
}
|
|
@@ -51,6 +51,17 @@ COSTS: dict[str, tuple[float, float]] = {
|
|
|
51
51
|
"o3-mini": (1.10, 4.40), # O3 Mini
|
|
52
52
|
"o4-mini": (1.10, 4.40), # O4 Mini
|
|
53
53
|
|
|
54
|
+
# ── Google Gemini ───────────────────────────────────────────
|
|
55
|
+
# Model (input, output)
|
|
56
|
+
"gemini-3.1-pro-preview": (1.25, 10.00), # Gemini 3.1 Pro
|
|
57
|
+
"gemini-2.5-pro": (1.25, 10.00), # Gemini 2.5 Pro
|
|
58
|
+
"gemini-2.5-flash": (0.30, 2.50), # Gemini 2.5 Flash
|
|
59
|
+
"gemini-2.5-flash-lite": (0.08, 0.60), # Gemini 2.5 Flash Lite (free tier eligible)
|
|
60
|
+
"gemini-2.0-flash": (0.10, 1.00), # Gemini 2.0 Flash
|
|
61
|
+
"gemini-2.0-flash-lite": (0.08, 0.60), # Gemini 2.0 Flash Lite
|
|
62
|
+
"gemini-1.5-pro": (1.25, 5.00), # Gemini 1.5 Pro
|
|
63
|
+
"gemini-1.5-flash": (0.08, 0.30), # Gemini 1.5 Flash
|
|
64
|
+
|
|
54
65
|
# ── OpenCode model aliases (full paths) ─────────────────────
|
|
55
66
|
"anthropic/claude-sonnet-4-20250514": (3.00, 15.00),
|
|
56
67
|
"anthropic/claude-opus-4-20250514": (15.00, 75.00),
|
|
@@ -59,6 +70,8 @@ COSTS: dict[str, tuple[float, float]] = {
|
|
|
59
70
|
"openai/gpt-4.1-mini": (0.40, 1.60),
|
|
60
71
|
"openai/o3": (10.50, 42.00),
|
|
61
72
|
"openai/o4-mini": (1.10, 4.40),
|
|
73
|
+
"google/gemini-2.5-flash": (0.30, 2.50),
|
|
74
|
+
"google/gemini-2.5-pro": (1.25, 10.00),
|
|
62
75
|
}
|
|
63
76
|
|
|
64
77
|
|
|
@@ -83,7 +96,7 @@ def estimate_cost(
|
|
|
83
96
|
output_tokens: Generated/completion tokens produced
|
|
84
97
|
cache_read_tokens: Tokens served from cache (discounted)
|
|
85
98
|
cache_write_tokens: Tokens written to cache (premium)
|
|
86
|
-
reasoning_tokens: Tokens spent on internal reasoning (GPT-o
|
|
99
|
+
reasoning_tokens: Tokens spent on internal reasoning (GPT-o, Gemini)
|
|
87
100
|
|
|
88
101
|
Returns:
|
|
89
102
|
Estimated cost in USD (0.0 for unknown/local models)
|
|
@@ -31,7 +31,7 @@ class OpenCodeProvider(BaseProvider):
|
|
|
31
31
|
# Below require the user's own provider credentials (`opencode auth login`).
|
|
32
32
|
"anthropic/claude-sonnet-5", "anthropic/claude-opus-4-8", "anthropic/claude-haiku-4-5",
|
|
33
33
|
"openai/gpt-5.4", "openai/gpt-4.1", "openai/gpt-4.1-mini", "openai/o3", "openai/o4-mini",
|
|
34
|
-
|
|
34
|
+
"google/gemini-2.5-flash", "google/gemini-2.5-pro",
|
|
35
35
|
]
|
|
36
36
|
|
|
37
37
|
def __init__(self):
|
|
@@ -85,5 +85,11 @@ def _auto_register():
|
|
|
85
85
|
except ImportError as e:
|
|
86
86
|
log.debug("vLLM provider not available: %s", e)
|
|
87
87
|
|
|
88
|
+
try:
|
|
89
|
+
from .gemini import GeminiProvider
|
|
90
|
+
register("gemini", GeminiProvider, aliases=["google", "gemini-pro", "gemini-flash"])
|
|
91
|
+
except ImportError as e:
|
|
92
|
+
log.debug("Gemini provider not available: %s", e)
|
|
93
|
+
|
|
88
94
|
|
|
89
95
|
_auto_register()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/bash_execution/__init__.py
RENAMED
|
File without changes
|
{kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/bash_execution/sessions.py
RENAMED
|
File without changes
|
{kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/code_search/__init__.py
RENAMED
|
File without changes
|
{kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/code_search/tools.py
RENAMED
|
File without changes
|
|
File without changes
|
{kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/file_operations/tools.py
RENAMED
|
File without changes
|
{kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/markdown/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/session_workflow/tools.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kivi_cli-2.0.6 → kivi_cli-2.0.7}/src/kiviai/services/tools/copilot_tools/web/store_tools.py
RENAMED
|
File without changes
|
|
File without changes
|