chore: update nanobot to 0.1.4.post6
This commit is contained in:
+24
-46
@@ -7,7 +7,7 @@ from jose import jwt, JWTError
|
||||
from pydantic import BaseModel, Field
|
||||
from app.core.security import SECRET_KEY, ALGORITHM
|
||||
from app.core.data_root import get_data_root
|
||||
from litellm import completion
|
||||
from app.core.llm_provider import build_llm_provider
|
||||
|
||||
router = APIRouter()
|
||||
security = HTTPBearer()
|
||||
@@ -154,52 +154,30 @@ def delete_llm_config(config_id: str, _: CurrentUser = Depends(get_admin_user)):
|
||||
return {"message": "LLM configuration deleted successfully"}
|
||||
|
||||
@router.post("/llm/test")
|
||||
def test_connection(request: TestConnectionRequest, _: CurrentUser = Depends(get_admin_user)):
|
||||
async def test_connection(request: TestConnectionRequest, _: CurrentUser = Depends(get_admin_user)):
|
||||
try:
|
||||
# Use litellm to test connection
|
||||
# litellm handles many providers
|
||||
kwargs = {
|
||||
"model": request.model,
|
||||
"messages": [{"role": "user", "content": "Hello"}],
|
||||
"max_tokens": 5
|
||||
provider = build_llm_provider(
|
||||
model=request.model.strip(),
|
||||
provider=request.provider,
|
||||
api_key=request.api_key,
|
||||
api_base=request.api_base,
|
||||
extra_headers=request.extra_headers,
|
||||
)
|
||||
response = await provider.chat(
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
max_tokens=5,
|
||||
temperature=0,
|
||||
)
|
||||
if response.finish_reason == "error":
|
||||
raise ValueError(response.content or "Unknown provider error")
|
||||
return {
|
||||
"success": True,
|
||||
"message": "Connection successful",
|
||||
"details": {
|
||||
"content": response.content,
|
||||
"finish_reason": response.finish_reason,
|
||||
"usage": response.usage,
|
||||
},
|
||||
}
|
||||
|
||||
if request.api_key:
|
||||
kwargs["api_key"] = request.api_key
|
||||
|
||||
if request.api_base:
|
||||
kwargs["api_base"] = request.api_base
|
||||
|
||||
if request.extra_headers:
|
||||
kwargs["extra_headers"] = request.extra_headers
|
||||
|
||||
# For OpenAI-compatible endpoints that are not standard OpenAI (like Local, vLLM etc)
|
||||
# usually user sets provider to "openai" and api_base to their custom URL.
|
||||
# litellm usually works well if we pass custom_llm_provider="openai" if provider is openai but custom url
|
||||
|
||||
# If provider is "local" or "openai", we generally use "openai" format
|
||||
if request.provider == "local":
|
||||
kwargs["custom_llm_provider"] = "openai"
|
||||
elif request.provider:
|
||||
kwargs["custom_llm_provider"] = request.provider
|
||||
|
||||
# If user explicitly selected provider in UI, we might want to respect that
|
||||
# But litellm completion main arg is 'model'.
|
||||
# If the UI 'model' input doesn't have prefix, we might need to add it or pass custom_llm_provider.
|
||||
|
||||
# Simple heuristic: if provider is set, try to pass it if litellm supports it or just rely on env vars/args
|
||||
# For this simple test, we just try to call it.
|
||||
|
||||
try:
|
||||
response = completion(**kwargs)
|
||||
except Exception as first_error:
|
||||
error_text = str(first_error)
|
||||
if request.provider and "Provider NOT provided" in error_text and "/" not in request.model:
|
||||
retry_kwargs = kwargs.copy()
|
||||
retry_kwargs["model"] = f"{request.provider}/{request.model}"
|
||||
response = completion(**retry_kwargs)
|
||||
else:
|
||||
raise first_error
|
||||
return {"success": True, "message": "Connection successful", "details": str(response)}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=f"Connection failed: {str(e)}")
|
||||
|
||||
@@ -114,6 +114,22 @@ def _save_data(data: List[Dict[str, Any]]):
|
||||
with open(DATA_FILE, "w") as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
def _dedupe_skills(data: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
||||
deduped: Dict[str, Dict[str, Any]] = {}
|
||||
for item in data:
|
||||
skill_id = str(item.get("id") or "").strip()
|
||||
if not skill_id:
|
||||
continue
|
||||
existing = deduped.get(skill_id)
|
||||
if existing is None:
|
||||
deduped[skill_id] = item
|
||||
continue
|
||||
existing_project = existing.get("project_id")
|
||||
incoming_project = item.get("project_id")
|
||||
if existing_project is None and incoming_project is not None:
|
||||
deduped[skill_id] = item
|
||||
return list(deduped.values())
|
||||
|
||||
def _safe_skill_dir_name(value: str) -> str:
|
||||
safe = re.sub(r'[^a-zA-Z0-9_\-]', '_', value or "").lower()
|
||||
return safe or "skill"
|
||||
@@ -183,9 +199,10 @@ def load_skills(project_id: Optional[int] = None) -> List[Dict[str, Any]]:
|
||||
data.append(new_skill)
|
||||
registered_paths.add(skill_dir)
|
||||
|
||||
deduped = _dedupe_skills(data)
|
||||
if project_id is not None:
|
||||
return [item for item in data if item.get("project_id") == project_id or item.get("project_id") is None]
|
||||
return data
|
||||
return [item for item in deduped if item.get("project_id") == project_id or item.get("project_id") is None]
|
||||
return deduped
|
||||
|
||||
@router.get("/skills", response_model=List[Skill])
|
||||
def list_skills(project_id: Optional[int] = None):
|
||||
@@ -384,7 +401,7 @@ def delete_skill(skill_id: str, project_id: Optional[int] = None):
|
||||
if item["id"] == skill_id:
|
||||
if item.get("is_builtin"):
|
||||
raise HTTPException(status_code=400, detail="Builtin skills cannot be deleted")
|
||||
if project_id is not None and item.get("project_id") != project_id:
|
||||
if project_id is not None and item.get("project_id") not in (project_id, None):
|
||||
new_data.append(item)
|
||||
continue
|
||||
found = True
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
from typing import List
|
||||
from fastapi import APIRouter, HTTPException, Depends
|
||||
from sqlalchemy.orm import Session
|
||||
from app.database import get_db
|
||||
from app.models.subagent import Subagent
|
||||
from app.models.project import Project
|
||||
from app.schemas.subagent import SubagentCreate, SubagentUpdate, Subagent as SubagentSchema
|
||||
from app.core.security import get_current_user, CurrentUser
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/projects/{project_id}/subagents", response_model=List[SubagentSchema])
|
||||
def list_subagents(
|
||||
project_id: int,
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: CurrentUser = Depends(get_current_user)
|
||||
):
|
||||
project = db.query(Project).filter(Project.id == project_id).first()
|
||||
if not project:
|
||||
raise HTTPException(status_code=404, detail="Project not found")
|
||||
|
||||
if not current_user.is_admin and project.owner_id != current_user.id:
|
||||
raise HTTPException(status_code=403, detail="Not enough permissions")
|
||||
|
||||
subagents = db.query(Subagent).filter(Subagent.project_id == project_id).offset(skip).limit(limit).all()
|
||||
return subagents
|
||||
|
||||
@router.post("/projects/{project_id}/subagents", response_model=SubagentSchema)
|
||||
def create_subagent(
|
||||
project_id: int,
|
||||
subagent: SubagentCreate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: CurrentUser = Depends(get_current_user)
|
||||
):
|
||||
project = db.query(Project).filter(Project.id == project_id).first()
|
||||
if not project:
|
||||
raise HTTPException(status_code=404, detail="Project not found")
|
||||
|
||||
if not current_user.is_admin and project.owner_id != current_user.id:
|
||||
raise HTTPException(status_code=403, detail="Not enough permissions")
|
||||
|
||||
db_subagent = Subagent(**subagent.dict(), project_id=project_id)
|
||||
db.add(db_subagent)
|
||||
db.commit()
|
||||
db.refresh(db_subagent)
|
||||
return db_subagent
|
||||
|
||||
@router.get("/subagents/{subagent_id}", response_model=SubagentSchema)
|
||||
def read_subagent(
|
||||
subagent_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: CurrentUser = Depends(get_current_user)
|
||||
):
|
||||
db_subagent = db.query(Subagent).filter(Subagent.id == subagent_id).first()
|
||||
if db_subagent is None:
|
||||
raise HTTPException(status_code=404, detail="Subagent not found")
|
||||
|
||||
project = db.query(Project).filter(Project.id == db_subagent.project_id).first()
|
||||
if not current_user.is_admin and project.owner_id != current_user.id:
|
||||
raise HTTPException(status_code=403, detail="Not enough permissions")
|
||||
|
||||
return db_subagent
|
||||
|
||||
@router.put("/subagents/{subagent_id}", response_model=SubagentSchema)
|
||||
def update_subagent(
|
||||
subagent_id: int,
|
||||
subagent: SubagentUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: CurrentUser = Depends(get_current_user)
|
||||
):
|
||||
db_subagent = db.query(Subagent).filter(Subagent.id == subagent_id).first()
|
||||
if db_subagent is None:
|
||||
raise HTTPException(status_code=404, detail="Subagent not found")
|
||||
|
||||
project = db.query(Project).filter(Project.id == db_subagent.project_id).first()
|
||||
if not current_user.is_admin and project.owner_id != current_user.id:
|
||||
raise HTTPException(status_code=403, detail="Not enough permissions")
|
||||
|
||||
subagent_data = subagent.dict(exclude_unset=True)
|
||||
for key, value in subagent_data.items():
|
||||
setattr(db_subagent, key, value)
|
||||
|
||||
db.add(db_subagent)
|
||||
db.commit()
|
||||
db.refresh(db_subagent)
|
||||
return db_subagent
|
||||
|
||||
@router.delete("/subagents/{subagent_id}")
|
||||
def delete_subagent(
|
||||
subagent_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: CurrentUser = Depends(get_current_user)
|
||||
):
|
||||
db_subagent = db.query(Subagent).filter(Subagent.id == subagent_id).first()
|
||||
if db_subagent is None:
|
||||
raise HTTPException(status_code=404, detail="Subagent not found")
|
||||
|
||||
project = db.query(Project).filter(Project.id == db_subagent.project_id).first()
|
||||
if not current_user.is_admin and project.owner_id != current_user.id:
|
||||
raise HTTPException(status_code=403, detail="Not enough permissions")
|
||||
|
||||
db.delete(db_subagent)
|
||||
db.commit()
|
||||
return {"status": "success"}
|
||||
Reference in New Issue
Block a user