versioning: 0.0.6, ux: move buttons, feat: add cloud providers, feat: increese tool call limit
Build Release EXE / build-windows-exe (release) Successful in 51s
Build Release EXE / build-windows-exe (release) Successful in 51s
This commit is contained in:
+102
-7
@@ -39,6 +39,13 @@ def resource_path(*parts: str) -> Path:
|
||||
class ChatRequest(BaseModel):
|
||||
message: str
|
||||
thread_id: str | None = DEFAULT_THREAD_ID
|
||||
images: list["ChatImageRequest"] = []
|
||||
|
||||
|
||||
class ChatImageRequest(BaseModel):
|
||||
name: str = "pasted-image.png"
|
||||
content_type: str = "image/png"
|
||||
image_data: str
|
||||
|
||||
|
||||
class ChatThreadRequest(BaseModel):
|
||||
@@ -114,12 +121,14 @@ def create_app() -> FastAPI:
|
||||
plan_runner = ContinualPlanRunner(plan_store, tools, memory)
|
||||
tools.plan_runner = plan_runner
|
||||
agent = OllamaAgent(
|
||||
settings.ollama_base_url,
|
||||
settings.ollama_model,
|
||||
settings.openai_base_url if settings.model_provider == "openai" else settings.ollama_base_url,
|
||||
settings.openai_model if settings.model_provider == "openai" else settings.ollama_model,
|
||||
tools,
|
||||
memory=memory,
|
||||
user_name=settings.traderai_user_name,
|
||||
num_ctx=settings.ollama_num_ctx,
|
||||
provider=settings.model_provider,
|
||||
api_key=settings.openai_api_key,
|
||||
)
|
||||
plan_runner.bind_agent(agent)
|
||||
scheduler.bind_agent(agent)
|
||||
@@ -171,6 +180,7 @@ def create_app() -> FastAPI:
|
||||
async def health() -> dict:
|
||||
return {
|
||||
"ollama": await agent.health(),
|
||||
"model_provider": settings.model_provider,
|
||||
"user": memory.get_profile(),
|
||||
"jobs": scheduler.list_jobs(),
|
||||
"app_data_dir": settings_payload()["app_data_dir"],
|
||||
@@ -190,7 +200,19 @@ def create_app() -> FastAPI:
|
||||
|
||||
@app.get("/api/ollama/status")
|
||||
async def ollama_status() -> dict:
|
||||
return await inspect_ollama()
|
||||
return await inspect_model_provider()
|
||||
|
||||
@app.get("/api/openai/models")
|
||||
async def openai_models() -> dict:
|
||||
status = await inspect_openai()
|
||||
return {
|
||||
"provider": "openai",
|
||||
"configured_model": status.get("configured_model"),
|
||||
"models": status.get("models", []),
|
||||
"message": status.get("message", ""),
|
||||
"detail": status.get("detail", ""),
|
||||
"online": status.get("online", False),
|
||||
}
|
||||
|
||||
@app.post("/api/ollama/launch")
|
||||
async def launch_ollama() -> dict:
|
||||
@@ -201,7 +223,7 @@ def create_app() -> FastAPI:
|
||||
popen_hidden(command)
|
||||
except OSError as exc:
|
||||
raise HTTPException(status_code=500, detail=f"Could not launch Ollama: {exc}") from exc
|
||||
status = await inspect_ollama()
|
||||
status = await inspect_model_provider()
|
||||
status["message"] = "Ollama launch requested."
|
||||
return status
|
||||
|
||||
@@ -218,7 +240,7 @@ def create_app() -> FastAPI:
|
||||
popen_hidden([str(cli), "pull", model])
|
||||
except OSError as exc:
|
||||
raise HTTPException(status_code=500, detail=f"Could not start model install: {exc}") from exc
|
||||
status = await inspect_ollama()
|
||||
status = await inspect_model_provider()
|
||||
status["message"] = f"Started installing model {model}."
|
||||
return status
|
||||
|
||||
@@ -298,14 +320,22 @@ def create_app() -> FastAPI:
|
||||
@app.post("/api/chat")
|
||||
async def chat(request: ChatRequest) -> dict:
|
||||
try:
|
||||
return await agent.chat(request.message, thread_id=request.thread_id)
|
||||
return await agent.chat(
|
||||
request.message,
|
||||
thread_id=request.thread_id,
|
||||
images=[image.model_dump() for image in request.images],
|
||||
)
|
||||
except OllamaUnavailable as exc:
|
||||
raise HTTPException(status_code=503, detail=str(exc)) from exc
|
||||
|
||||
@app.post("/api/chat/stream")
|
||||
async def chat_stream(request: ChatRequest) -> StreamingResponse:
|
||||
async def events():
|
||||
async for event in agent.chat_events(request.message, thread_id=request.thread_id):
|
||||
async for event in agent.chat_events(
|
||||
request.message,
|
||||
thread_id=request.thread_id,
|
||||
images=[image.model_dump() for image in request.images],
|
||||
):
|
||||
yield f"data: {json.dumps(event)}\n\n"
|
||||
|
||||
return StreamingResponse(events(), media_type="text/event-stream")
|
||||
@@ -475,6 +505,60 @@ def negotiation_identifier_params(identifier: str) -> dict[str, Any]:
|
||||
return {"hash": value}
|
||||
|
||||
|
||||
async def inspect_model_provider() -> dict[str, Any]:
|
||||
settings = get_settings()
|
||||
if settings.model_provider == "openai":
|
||||
return await inspect_openai()
|
||||
return await inspect_ollama()
|
||||
|
||||
|
||||
async def inspect_openai() -> dict[str, Any]:
|
||||
settings = get_settings()
|
||||
models: list[str] = []
|
||||
online = False
|
||||
detail = ""
|
||||
if not settings.openai_api_key:
|
||||
return {
|
||||
"installed": True,
|
||||
"running": False,
|
||||
"online": False,
|
||||
"provider": "openai",
|
||||
"model_available": False,
|
||||
"configured_model": settings.openai_model,
|
||||
"base_url": settings.openai_base_url,
|
||||
"models": [],
|
||||
"message": "OpenAI is selected, but no API key is configured.",
|
||||
"detail": "",
|
||||
}
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=10) as client:
|
||||
response = await client.get(
|
||||
f"{settings.openai_base_url.rstrip('/')}/models",
|
||||
headers={"Authorization": f"Bearer {settings.openai_api_key}"},
|
||||
)
|
||||
response.raise_for_status()
|
||||
body = response.json()
|
||||
online = True
|
||||
models = sorted(item.get("id") for item in body.get("data", []) if item.get("id"))
|
||||
except (httpx.HTTPError, ValueError) as exc:
|
||||
detail = str(exc)
|
||||
|
||||
model_available = settings.openai_model in models
|
||||
return {
|
||||
"installed": True,
|
||||
"running": online,
|
||||
"online": online,
|
||||
"provider": "openai",
|
||||
"model_available": model_available,
|
||||
"configured_model": settings.openai_model,
|
||||
"base_url": settings.openai_base_url,
|
||||
"models": models,
|
||||
"message": openai_status_message(online, bool(settings.openai_api_key), model_available, settings.openai_model),
|
||||
"detail": detail,
|
||||
}
|
||||
|
||||
|
||||
async def inspect_ollama() -> dict[str, Any]:
|
||||
settings = get_settings()
|
||||
executable = find_ollama_executable()
|
||||
@@ -500,6 +584,7 @@ async def inspect_ollama() -> dict[str, Any]:
|
||||
"installed": installed,
|
||||
"running": online,
|
||||
"online": online,
|
||||
"provider": "ollama",
|
||||
"model_available": model_available,
|
||||
"configured_model": settings.ollama_model,
|
||||
"base_url": settings.ollama_base_url,
|
||||
@@ -514,6 +599,16 @@ async def inspect_ollama() -> dict[str, Any]:
|
||||
}
|
||||
|
||||
|
||||
def openai_status_message(running: bool, configured: bool, model_available: bool, model: str) -> str:
|
||||
if not configured:
|
||||
return "OpenAI API key is not configured."
|
||||
if not running:
|
||||
return "OpenAI is not reachable with the configured key."
|
||||
if not model_available:
|
||||
return f'OpenAI is reachable, but model "{model}" was not returned by the API.'
|
||||
return "OpenAI is ready."
|
||||
|
||||
|
||||
def ollama_status_message(installed: bool, running: bool, model_available: bool, model: str) -> str:
|
||||
if not installed:
|
||||
return "Ollama is not installed."
|
||||
|
||||
Reference in New Issue
Block a user