Files
microdao-daarion/sofia_api.py
Apple 744c149300
Some checks failed
Build and Deploy Docs / build-and-deploy (push) Has been cancelled
Add automated session logging system
- Created logs/ structure (sessions, operations, incidents)
- Added session-start/log/end scripts
- Installed Git hooks for auto-logging commits/pushes
- Added shell integration for zsh
- Created CHANGELOG.md
- Documented today's session (2026-01-10)
2026-01-10 04:53:17 -08:00

240 lines
7.6 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Sofia API - FastAPI Backend
Provides web API for Sofia agent
"""
import asyncio
import os
from typing import Optional
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from dotenv import load_dotenv
import httpx
# Load environment
load_dotenv()
try:
from openai import OpenAI
except ImportError:
OpenAI = None
# ============================================================================
# Configuration
# ============================================================================
SOFIA_SYSTEM_PROMPT = """Ти - Sofia, Chief AI Engineer & R&D Orchestrator в екосистемі DAARION.city.
Твоя роль:
- Керування дослідженнями та експериментами з AI/ML
- Координація R&D лабораторії
- Технічне лідерство в AI проектах
- Оркестрація команди дослідницьких агентів
Характеристики:
- Глибока технічна експертиза в AI/ML
- Стратегічне мислення
- Інноваційний підхід до вирішення проблем
- Лідерські навички
Стиль спілкування:
- Професійний, але дружній
- Чіткий та технічно точний
- Готова пояснити складні концепції
- Підтримуюча та надихаюча
Відповідай українською мовою, якщо не вказано інше.
"""
XAI_API_KEY = os.getenv("XAI_API_KEY", "")
XAI_BASE_URL = os.getenv("XAI_BASE_URL", "https://api.x.ai/v1")
XAI_MODEL = os.getenv("XAI_MODEL", "grok-beta")
OLLAMA_BASE_URL = os.getenv("OLLAMA_BASE_URL", "http://localhost:11434")
OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "qwen2.5-coder:32b")
# ============================================================================
# FastAPI App
# ============================================================================
app = FastAPI(
title="Sofia API",
version="1.0.0",
description="Web API for Sofia - Chief AI Engineer"
)
# CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # В production обмежити
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ============================================================================
# Models
# ============================================================================
class ChatRequest(BaseModel):
message: str
class ChatResponse(BaseModel):
response: str
tokens: Optional[int] = None
model: str
provider: str
# ============================================================================
# Sofia Logic
# ============================================================================
conversation_history = []
async def chat_with_grok(message: str) -> dict:
"""Chat using Grok API"""
if not OpenAI:
raise HTTPException(status_code=500, detail="OpenAI library not installed")
if not XAI_API_KEY:
raise HTTPException(status_code=500, detail="XAI_API_KEY not configured")
try:
client = OpenAI(api_key=XAI_API_KEY, base_url=XAI_BASE_URL)
messages = [{"role": "system", "content": SOFIA_SYSTEM_PROMPT}]
messages.extend(conversation_history[-10:])
messages.append({"role": "user", "content": message})
response = client.chat.completions.create(
model=XAI_MODEL,
messages=messages,
temperature=0.7,
max_tokens=2000
)
reply = response.choices[0].message.content
tokens = response.usage.total_tokens
# Save to history
conversation_history.append({"role": "user", "content": message})
conversation_history.append({"role": "assistant", "content": reply})
return {
"response": reply,
"tokens": tokens,
"model": XAI_MODEL,
"provider": "grok"
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Grok API error: {str(e)}")
async def chat_with_ollama(message: str) -> dict:
"""Chat using local Ollama"""
try:
# Prepare prompt
full_prompt = SOFIA_SYSTEM_PROMPT + "\n\n"
for msg in conversation_history[-6:]:
role = "Користувач" if msg["role"] == "user" else "Sofia"
full_prompt += f"{role}: {msg['content']}\n"
full_prompt += f"Користувач: {message}\nSofia:"
# Call Ollama
async with httpx.AsyncClient(timeout=120.0) as client:
response = await client.post(
f"{OLLAMA_BASE_URL}/api/generate",
json={
"model": OLLAMA_MODEL,
"prompt": full_prompt,
"stream": False,
"options": {
"temperature": 0.7,
"num_predict": 1000
}
}
)
response.raise_for_status()
result = response.json()
reply = result.get("response", "").strip()
tokens = result.get("eval_count", 0)
# Save to history
conversation_history.append({"role": "user", "content": message})
conversation_history.append({"role": "assistant", "content": reply})
return {
"response": reply,
"tokens": tokens,
"model": OLLAMA_MODEL,
"provider": "ollama"
}
except httpx.ConnectError:
raise HTTPException(
status_code=503,
detail="Cannot connect to Ollama. Make sure it's running: ollama serve"
)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Ollama error: {str(e)}")
# ============================================================================
# Routes
# ============================================================================
@app.get("/health")
async def health():
"""Health check"""
return {
"status": "healthy",
"service": "sofia-api",
"grok_available": bool(XAI_API_KEY),
"ollama_url": OLLAMA_BASE_URL,
"model": OLLAMA_MODEL if not XAI_API_KEY else XAI_MODEL
}
@app.post("/chat", response_model=ChatResponse)
async def chat(request: ChatRequest):
"""Chat with Sofia"""
if not request.message.strip():
raise HTTPException(status_code=400, detail="Message cannot be empty")
# Use Grok if API key available, otherwise Ollama
if XAI_API_KEY:
result = await chat_with_grok(request.message)
else:
result = await chat_with_ollama(request.message)
return ChatResponse(**result)
@app.post("/clear")
async def clear_history():
"""Clear conversation history"""
global conversation_history
conversation_history = []
return {"message": "History cleared"}
@app.get("/history")
async def get_history():
"""Get conversation history"""
return {"history": conversation_history}
# ============================================================================
# Main
# ============================================================================
if __name__ == "__main__":
import uvicorn
print("🤖 Sofia API Starting...")
print(f" Mode: {'Grok API' if XAI_API_KEY else 'Ollama (local)'}")
print(f" Model: {XAI_MODEL if XAI_API_KEY else OLLAMA_MODEL}")
print(f" URL: http://localhost:8899")
print()
uvicorn.run(app, host="0.0.0.0", port=8899)