virtuai-cli 0.8.7__tar.gz → 0.8.8__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.
- {virtuai_cli-0.8.7 → virtuai_cli-0.8.8}/PKG-INFO +3 -1
- {virtuai_cli-0.8.7 → virtuai_cli-0.8.8}/pyproject.toml +9 -1
- {virtuai_cli-0.8.7 → virtuai_cli-0.8.8}/src/virtuai_cli/chat/ask.py +4 -0
- {virtuai_cli-0.8.7 → virtuai_cli-0.8.8}/src/virtuai_cli/chat/tui.py +4 -0
- {virtuai_cli-0.8.7 → virtuai_cli-0.8.8}/src/virtuai_cli/runner.py +87 -0
- {virtuai_cli-0.8.7 → virtuai_cli-0.8.8}/src/virtuai_cli.egg-info/PKG-INFO +3 -1
- {virtuai_cli-0.8.7 → virtuai_cli-0.8.8}/src/virtuai_cli.egg-info/requires.txt +3 -0
- {virtuai_cli-0.8.7 → virtuai_cli-0.8.8}/README.md +0 -0
- {virtuai_cli-0.8.7 → virtuai_cli-0.8.8}/setup.cfg +0 -0
- {virtuai_cli-0.8.7 → virtuai_cli-0.8.8}/src/virtuai_cli/__init__.py +0 -0
- {virtuai_cli-0.8.7 → virtuai_cli-0.8.8}/src/virtuai_cli/chat/__init__.py +0 -0
- {virtuai_cli-0.8.7 → virtuai_cli-0.8.8}/src/virtuai_cli/chat/command.py +0 -0
- {virtuai_cli-0.8.7 → virtuai_cli-0.8.8}/src/virtuai_cli/chat/history.py +0 -0
- {virtuai_cli-0.8.7 → virtuai_cli-0.8.8}/src/virtuai_cli/chat/sse.py +0 -0
- {virtuai_cli-0.8.7 → virtuai_cli-0.8.8}/src/virtuai_cli/chat/widgets.py +0 -0
- {virtuai_cli-0.8.7 → virtuai_cli-0.8.8}/src/virtuai_cli/config.py +0 -0
- {virtuai_cli-0.8.7 → virtuai_cli-0.8.8}/src/virtuai_cli/executor.py +0 -0
- {virtuai_cli-0.8.7 → virtuai_cli-0.8.8}/src/virtuai_cli/main.py +0 -0
- {virtuai_cli-0.8.7 → virtuai_cli-0.8.8}/src/virtuai_cli/security.py +0 -0
- {virtuai_cli-0.8.7 → virtuai_cli-0.8.8}/src/virtuai_cli.egg-info/SOURCES.txt +0 -0
- {virtuai_cli-0.8.7 → virtuai_cli-0.8.8}/src/virtuai_cli.egg-info/dependency_links.txt +0 -0
- {virtuai_cli-0.8.7 → virtuai_cli-0.8.8}/src/virtuai_cli.egg-info/entry_points.txt +0 -0
- {virtuai_cli-0.8.7 → virtuai_cli-0.8.8}/src/virtuai_cli.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: virtuai-cli
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.8
|
|
4
4
|
Summary: Run VirtuAI deep agents on your local machine
|
|
5
5
|
Author-email: uCloudStore <lmoreno@ucloudstore.com>
|
|
6
6
|
License: Proprietary
|
|
@@ -20,6 +20,8 @@ Requires-Dist: keyring>=25.0
|
|
|
20
20
|
Requires-Dist: typer>=0.12
|
|
21
21
|
Requires-Dist: rich>=13.0
|
|
22
22
|
Requires-Dist: textual>=0.86
|
|
23
|
+
Provides-Extra: local
|
|
24
|
+
Requires-Dist: langchain-ollama>=0.2; extra == "local"
|
|
23
25
|
|
|
24
26
|
# VirtuAI CLI
|
|
25
27
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "virtuai-cli"
|
|
7
|
-
version = "0.8.
|
|
7
|
+
version = "0.8.8"
|
|
8
8
|
description = "Run VirtuAI deep agents on your local machine"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -30,6 +30,14 @@ dependencies = [
|
|
|
30
30
|
"textual>=0.86",
|
|
31
31
|
]
|
|
32
32
|
|
|
33
|
+
# Optional: local model inference. Pulls langchain-ollama (+ langchain-core) so
|
|
34
|
+
# the CLI can answer the server's model_generate frames from a locally-running
|
|
35
|
+
# Ollama. Install with: pip install 'virtuai-cli[local]'
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
local = [
|
|
38
|
+
"langchain-ollama>=0.2",
|
|
39
|
+
]
|
|
40
|
+
|
|
33
41
|
[project.urls]
|
|
34
42
|
Homepage = "https://imvituai.com"
|
|
35
43
|
|
|
@@ -134,6 +134,10 @@ async def _run_ask(
|
|
|
134
134
|
err.print(f"[dim]✓ {first[0][:80] if first else ''}[/dim]")
|
|
135
135
|
else:
|
|
136
136
|
err.print("[dim]✓ done[/dim]")
|
|
137
|
+
elif etype == "notice" and mode == "stream":
|
|
138
|
+
msg = event.get("message", "")
|
|
139
|
+
color = {"warn": "yellow", "error": "red"}.get(event.get("level"), "dim")
|
|
140
|
+
err.print(f"[{color}]⚠ {msg}[/{color}]")
|
|
137
141
|
elif etype == "error":
|
|
138
142
|
msg = event.get("error") or event.get("message") or "unknown"
|
|
139
143
|
err.print(f"[red]error: {msg}[/red]")
|
|
@@ -871,6 +871,10 @@ class ChatApp(App):
|
|
|
871
871
|
"\n\n*[response truncated — try raising the agent's max_tokens]*"
|
|
872
872
|
)
|
|
873
873
|
turn._truncated_shown = True
|
|
874
|
+
elif etype == "notice":
|
|
875
|
+
msg = event.get("message") or ""
|
|
876
|
+
if msg:
|
|
877
|
+
await turn.append_token(f"\n\n*⚠ {msg}*\n\n")
|
|
874
878
|
elif etype == "error":
|
|
875
879
|
msg = event.get("error") or event.get("message") or "unknown error"
|
|
876
880
|
await turn.append_token(f"\n\n*[error: {msg}]*")
|
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import json
|
|
7
7
|
import logging
|
|
8
|
+
import os
|
|
8
9
|
import platform
|
|
9
10
|
import sys
|
|
10
11
|
import time
|
|
@@ -13,6 +14,9 @@ from typing import Callable, Optional
|
|
|
13
14
|
|
|
14
15
|
_AUDIT_DIR = Path.home() / ".virtuai"
|
|
15
16
|
|
|
17
|
+
# Local Ollama endpoint used to answer the server's model_generate frames.
|
|
18
|
+
OLLAMA_BASE_URL = os.environ.get("OLLAMA_BASE_URL", "http://localhost:11434")
|
|
19
|
+
|
|
16
20
|
import websockets
|
|
17
21
|
from rich.console import Console
|
|
18
22
|
|
|
@@ -101,6 +105,84 @@ def _default_on_status(level: str, msg: str) -> None:
|
|
|
101
105
|
console.print(f"[{style}]{msg}[/{style}]" if style else msg)
|
|
102
106
|
|
|
103
107
|
|
|
108
|
+
async def _probe_ollama() -> dict:
|
|
109
|
+
"""Return {"reachable": bool, "models": [name, ...]} for the local Ollama.
|
|
110
|
+
|
|
111
|
+
Reported in the ready frame so the server knows up-front whether it can
|
|
112
|
+
proxy a model turn here or should fall back to a cloud model.
|
|
113
|
+
"""
|
|
114
|
+
try:
|
|
115
|
+
import httpx
|
|
116
|
+
except Exception:
|
|
117
|
+
return {"reachable": False, "models": []}
|
|
118
|
+
try:
|
|
119
|
+
async with httpx.AsyncClient(timeout=2.0) as client:
|
|
120
|
+
resp = await client.get(f"{OLLAMA_BASE_URL.rstrip('/')}/api/tags")
|
|
121
|
+
resp.raise_for_status()
|
|
122
|
+
data = resp.json()
|
|
123
|
+
models = [m.get("name", "") for m in data.get("models", []) if m.get("name")]
|
|
124
|
+
return {"reachable": True, "models": models}
|
|
125
|
+
except Exception:
|
|
126
|
+
return {"reachable": False, "models": []}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
async def _handle_model_generate(frame: dict, ws, on_status: StatusCallback) -> None:
|
|
130
|
+
"""Answer a server model_generate frame from the local Ollama.
|
|
131
|
+
|
|
132
|
+
Streams text deltas as model_token frames, then a terminal model_result
|
|
133
|
+
carrying tool_calls + usage. On any failure replies model_error so the
|
|
134
|
+
server can fall back to a cloud model.
|
|
135
|
+
"""
|
|
136
|
+
fid = frame.get("id")
|
|
137
|
+
|
|
138
|
+
async def _error(reason: str) -> None:
|
|
139
|
+
await ws.send(json.dumps({
|
|
140
|
+
"v": 1, "id": fid, "type": "model_error", "error": reason,
|
|
141
|
+
}))
|
|
142
|
+
|
|
143
|
+
try:
|
|
144
|
+
from langchain_ollama import ChatOllama
|
|
145
|
+
from langchain_core.messages import messages_from_dict
|
|
146
|
+
except Exception:
|
|
147
|
+
await _error(
|
|
148
|
+
"local model support not installed — run: pip install 'virtuai-cli[local]'"
|
|
149
|
+
)
|
|
150
|
+
return
|
|
151
|
+
|
|
152
|
+
model = frame.get("model") or ""
|
|
153
|
+
on_status("info", f"local model: {model}")
|
|
154
|
+
try:
|
|
155
|
+
messages = messages_from_dict(frame.get("messages", []))
|
|
156
|
+
tools = frame.get("tools") or []
|
|
157
|
+
kwargs = {"model": model, "base_url": OLLAMA_BASE_URL}
|
|
158
|
+
if frame.get("temperature") is not None:
|
|
159
|
+
kwargs["temperature"] = frame["temperature"]
|
|
160
|
+
if frame.get("num_predict") is not None:
|
|
161
|
+
kwargs["num_predict"] = frame["num_predict"]
|
|
162
|
+
llm = ChatOllama(**kwargs)
|
|
163
|
+
if tools:
|
|
164
|
+
llm = llm.bind_tools(tools)
|
|
165
|
+
|
|
166
|
+
gathered = None
|
|
167
|
+
async for chunk in llm.astream(messages):
|
|
168
|
+
gathered = chunk if gathered is None else gathered + chunk
|
|
169
|
+
text = chunk.content if isinstance(chunk.content, str) else ""
|
|
170
|
+
if text:
|
|
171
|
+
await ws.send(json.dumps({
|
|
172
|
+
"v": 1, "id": fid, "type": "model_token", "content": text,
|
|
173
|
+
}))
|
|
174
|
+
|
|
175
|
+
tool_calls = list(getattr(gathered, "tool_calls", []) or []) if gathered else []
|
|
176
|
+
usage = dict(getattr(gathered, "usage_metadata", None) or {}) if gathered else {}
|
|
177
|
+
await ws.send(json.dumps({
|
|
178
|
+
"v": 1, "id": fid, "type": "model_result",
|
|
179
|
+
"tool_calls": tool_calls, "usage": usage,
|
|
180
|
+
}))
|
|
181
|
+
except Exception as exc:
|
|
182
|
+
logger.warning("model_generate failed: %s", exc)
|
|
183
|
+
await _error(str(exc))
|
|
184
|
+
|
|
185
|
+
|
|
104
186
|
async def _handle_frame(
|
|
105
187
|
frame: dict,
|
|
106
188
|
workdir: Path,
|
|
@@ -124,11 +206,16 @@ async def _handle_frame(
|
|
|
124
206
|
"workdir": str(workdir),
|
|
125
207
|
"local_skills": _read_local_skills(workdir),
|
|
126
208
|
"local_memory": _read_local_memory(workdir),
|
|
209
|
+
"ollama": await _probe_ollama(),
|
|
127
210
|
}
|
|
128
211
|
await ws.send(json.dumps(reply))
|
|
129
212
|
on_status("info", f"Connected. Workdir: {workdir}")
|
|
130
213
|
return
|
|
131
214
|
|
|
215
|
+
if ftype == "model_generate":
|
|
216
|
+
await _handle_model_generate(frame, ws, on_status)
|
|
217
|
+
return
|
|
218
|
+
|
|
132
219
|
if ftype == "ping":
|
|
133
220
|
await ws.send(json.dumps({"v": 1, "id": fid, "type": "pong", "ts": time.time()}))
|
|
134
221
|
return
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: virtuai-cli
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.8
|
|
4
4
|
Summary: Run VirtuAI deep agents on your local machine
|
|
5
5
|
Author-email: uCloudStore <lmoreno@ucloudstore.com>
|
|
6
6
|
License: Proprietary
|
|
@@ -20,6 +20,8 @@ Requires-Dist: keyring>=25.0
|
|
|
20
20
|
Requires-Dist: typer>=0.12
|
|
21
21
|
Requires-Dist: rich>=13.0
|
|
22
22
|
Requires-Dist: textual>=0.86
|
|
23
|
+
Provides-Extra: local
|
|
24
|
+
Requires-Dist: langchain-ollama>=0.2; extra == "local"
|
|
23
25
|
|
|
24
26
|
# VirtuAI CLI
|
|
25
27
|
|
|
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
|