Spaces:
Running
Running
Sohan Kshirsagar commited on
Commit ·
c48f478
1
Parent(s): 7b6fb1a
Changes to include single model represented as multiple personas
Browse files- README.md +3 -1
- multi_llm_chatbot_backend/app/__init__.py +0 -0
- multi_llm_chatbot_backend/app/api/routes.py +40 -0
- multi_llm_chatbot_backend/app/config.py +0 -0
- multi_llm_chatbot_backend/app/core/context.py +0 -0
- multi_llm_chatbot_backend/app/core/orchestrator.py +24 -0
- multi_llm_chatbot_backend/app/llm/llm_client.py +7 -0
- multi_llm_chatbot_backend/app/llm/mistral_client.py +25 -0
- multi_llm_chatbot_backend/app/main.py +14 -0
- multi_llm_chatbot_backend/app/models/persona.py +11 -0
- multi_llm_chatbot_backend/app/tests/test_mistral.py +13 -0
- multi_llm_chatbot_backend/requirements.txt +3 -0
README.md
CHANGED
|
@@ -1 +1,3 @@
|
|
| 1 |
-
# Neon-AI-Project
|
|
|
|
|
|
|
|
|
| 1 |
+
# Neon-AI-Project
|
| 2 |
+
|
| 3 |
+
Multi-model LLM chatbot.
|
multi_llm_chatbot_backend/app/__init__.py
ADDED
|
File without changes
|
multi_llm_chatbot_backend/app/api/routes.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import APIRouter, Body
|
| 2 |
+
from app.llm.mistral_client import MistralClient
|
| 3 |
+
from app.models.persona import Persona
|
| 4 |
+
from app.core.orchestrator import ChatOrchestrator
|
| 5 |
+
|
| 6 |
+
router = APIRouter()
|
| 7 |
+
|
| 8 |
+
# Singleton for now
|
| 9 |
+
llm = MistralClient()
|
| 10 |
+
orchestrator = ChatOrchestrator()
|
| 11 |
+
|
| 12 |
+
# Register initial personas
|
| 13 |
+
orchestrator.register_persona(Persona(
|
| 14 |
+
id="methodist",
|
| 15 |
+
name="Methodist Advisor",
|
| 16 |
+
system_prompt="You are a highly methodical PhD advisor who believes in structure and planning.",
|
| 17 |
+
llm=llm
|
| 18 |
+
))
|
| 19 |
+
|
| 20 |
+
orchestrator.register_persona(Persona(
|
| 21 |
+
id="theorist",
|
| 22 |
+
name="Theorist Advisor",
|
| 23 |
+
system_prompt="You are a philosophical PhD advisor who focuses on abstract theories and ideas.",
|
| 24 |
+
llm=llm
|
| 25 |
+
))
|
| 26 |
+
|
| 27 |
+
orchestrator.register_persona(Persona(
|
| 28 |
+
id="pragmatist",
|
| 29 |
+
name="Pragmatist Advisor",
|
| 30 |
+
system_prompt="You are a practical PhD advisor who focuses on real-world outcomes and utility.",
|
| 31 |
+
llm=llm
|
| 32 |
+
))
|
| 33 |
+
|
| 34 |
+
@router.post("/chat")
|
| 35 |
+
async def chat(
|
| 36 |
+
user_input: str = Body(...),
|
| 37 |
+
active_personas: list[str] = Body(default=["methodist", "theorist", "pragmatist"])
|
| 38 |
+
):
|
| 39 |
+
orchestrator.set_active_personas(active_personas)
|
| 40 |
+
return await orchestrator.process_user_input(user_input)
|
multi_llm_chatbot_backend/app/config.py
ADDED
|
File without changes
|
multi_llm_chatbot_backend/app/core/context.py
ADDED
|
File without changes
|
multi_llm_chatbot_backend/app/core/orchestrator.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from app.models.persona import Persona
|
| 2 |
+
|
| 3 |
+
class ChatOrchestrator:
|
| 4 |
+
def __init__(self):
|
| 5 |
+
self.history: list[dict] = []
|
| 6 |
+
self.personas: dict[str, Persona] = {}
|
| 7 |
+
|
| 8 |
+
def register_persona(self, persona: Persona):
|
| 9 |
+
self.personas[persona.id] = persona
|
| 10 |
+
|
| 11 |
+
def set_active_personas(self, ids: list[str]):
|
| 12 |
+
self.active_ids = [pid for pid in ids if pid in self.personas]
|
| 13 |
+
|
| 14 |
+
async def process_user_input(self, user_input: str):
|
| 15 |
+
self.history.append({"role": "user", "content": user_input})
|
| 16 |
+
responses = []
|
| 17 |
+
|
| 18 |
+
for pid in self.active_ids:
|
| 19 |
+
persona = self.personas[pid]
|
| 20 |
+
reply = await persona.respond(self.history)
|
| 21 |
+
self.history.append({"role": persona.id, "content": reply})
|
| 22 |
+
responses.append({"persona": persona.name, "response": reply})
|
| 23 |
+
|
| 24 |
+
return responses
|
multi_llm_chatbot_backend/app/llm/llm_client.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from abc import ABC, abstractmethod
|
| 2 |
+
from typing import List
|
| 3 |
+
|
| 4 |
+
class LLMClient(ABC):
|
| 5 |
+
@abstractmethod
|
| 6 |
+
async def generate(self, system_prompt: str, context: List[dict]) -> str:
|
| 7 |
+
pass
|
multi_llm_chatbot_backend/app/llm/mistral_client.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import httpx
|
| 2 |
+
from typing import List
|
| 3 |
+
from app.llm.llm_client import LLMClient
|
| 4 |
+
|
| 5 |
+
OLLAMA_URL = "http://localhost:11434/api/generate"
|
| 6 |
+
|
| 7 |
+
class MistralClient(LLMClient):
|
| 8 |
+
async def generate(self, system_prompt: str, context: List[dict]) -> str:
|
| 9 |
+
# Flatten context into a string
|
| 10 |
+
formatted = "\n".join(f"{msg['role'].capitalize()}: {msg['content']}" for msg in context)
|
| 11 |
+
full_prompt = f"{system_prompt}\n\n{formatted}\n\nAssistant:"
|
| 12 |
+
|
| 13 |
+
payload = {
|
| 14 |
+
"model": "mistral",
|
| 15 |
+
"prompt": full_prompt,
|
| 16 |
+
"stream": False
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
try:
|
| 20 |
+
async with httpx.AsyncClient(timeout=60.0) as client:
|
| 21 |
+
response = await client.post(OLLAMA_URL, json=payload)
|
| 22 |
+
response.raise_for_status()
|
| 23 |
+
return response.json().get("response", "[No response]")
|
| 24 |
+
except httpx.HTTPError as e:
|
| 25 |
+
return f"[Error from Mistral: {str(e)}]"
|
multi_llm_chatbot_backend/app/main.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI
|
| 2 |
+
from app.api.routes import router
|
| 3 |
+
|
| 4 |
+
app = FastAPI(
|
| 5 |
+
title="Multi-LLM Chatbot Backend",
|
| 6 |
+
version="0.1"
|
| 7 |
+
)
|
| 8 |
+
|
| 9 |
+
# Include route definitions
|
| 10 |
+
app.include_router(router)
|
| 11 |
+
|
| 12 |
+
@app.get("/")
|
| 13 |
+
def root():
|
| 14 |
+
return {"message": "Backend is up and running"}
|
multi_llm_chatbot_backend/app/models/persona.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from app.llm.llm_client import LLMClient
|
| 2 |
+
|
| 3 |
+
class Persona:
|
| 4 |
+
def __init__(self, id: str, name: str, system_prompt: str, llm: LLMClient):
|
| 5 |
+
self.id = id
|
| 6 |
+
self.name = name
|
| 7 |
+
self.system_prompt = system_prompt
|
| 8 |
+
self.llm = llm
|
| 9 |
+
|
| 10 |
+
async def respond(self, context: list[dict]) -> str:
|
| 11 |
+
return await self.llm.generate(self.system_prompt, context)
|
multi_llm_chatbot_backend/app/tests/test_mistral.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import requests
|
| 2 |
+
|
| 3 |
+
url = "http://localhost:11434/api/generate"
|
| 4 |
+
|
| 5 |
+
payload = {
|
| 6 |
+
"model": "mistral",
|
| 7 |
+
"prompt": "You are a professor. Give me PhD advice.",
|
| 8 |
+
"stream": False
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
response = requests.post(url, json=payload)
|
| 12 |
+
print(response.status_code)
|
| 13 |
+
print(response.json())
|
multi_llm_chatbot_backend/requirements.txt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi
|
| 2 |
+
uvicorn
|
| 3 |
+
httpx
|