mirror of
http://88.130.71.182:3000/BlitTech/contexta_be.git
synced 2026-06-12 23:23:21 +00:00
688 lines
20 KiB
Python
688 lines
20 KiB
Python
import zipfile
|
|
import io
|
|
import json
|
|
from typing import Dict, Any
|
|
|
|
|
|
def generate_export_package(
|
|
chatbot: Dict[str, Any],
|
|
company: Dict[str, Any],
|
|
qdrant_url: str,
|
|
qdrant_key: str,
|
|
) -> bytes:
|
|
"""
|
|
Generate a complete export ZIP with FastAPI backend + React widget
|
|
"""
|
|
buffer = io.BytesIO()
|
|
|
|
with zipfile.ZipFile(buffer, "w", zipfile.ZIP_DEFLATED) as zf:
|
|
# ── Backend files ──────────────────────────────────────────
|
|
zf.writestr("backend/requirements.txt", _requirements())
|
|
zf.writestr("backend/.env.example", _env_example(chatbot, qdrant_url, qdrant_key))
|
|
zf.writestr("backend/main.py", _main_py(chatbot))
|
|
zf.writestr("backend/rag_engine.py", _rag_engine_py())
|
|
zf.writestr("backend/Dockerfile", _dockerfile())
|
|
zf.writestr("backend/docker-compose.yml", _docker_compose(chatbot))
|
|
zf.writestr("backend/README.md", _backend_readme(chatbot))
|
|
|
|
# ── Frontend files ─────────────────────────────────────────
|
|
zf.writestr("frontend/src/ChatWidget.tsx", _chat_widget_tsx(chatbot))
|
|
zf.writestr("frontend/src/useChat.ts", _use_chat_ts())
|
|
zf.writestr("frontend/src/api.ts", _api_ts())
|
|
zf.writestr("frontend/src/types.ts", _types_ts())
|
|
zf.writestr("frontend/package.json", _package_json(chatbot))
|
|
zf.writestr("frontend/tsconfig.json", _tsconfig())
|
|
zf.writestr("frontend/vite.config.ts", _vite_config())
|
|
zf.writestr("frontend/README.md", _frontend_readme(chatbot))
|
|
|
|
# ── Root ───────────────────────────────────────────────────
|
|
zf.writestr("QUICK_START.md", _quick_start(chatbot))
|
|
zf.writestr("setup.py", _setup_wizard(chatbot))
|
|
|
|
buffer.seek(0)
|
|
return buffer.read()
|
|
|
|
|
|
def _requirements():
|
|
return """fastapi==0.115.0
|
|
uvicorn[standard]==0.30.6
|
|
python-dotenv==1.0.1
|
|
pydantic==2.8.2
|
|
qdrant-client==1.11.1
|
|
openai==1.51.0
|
|
anthropic==0.34.2
|
|
google-generativeai==0.8.1
|
|
httpx==0.27.2
|
|
langdetect==1.0.9
|
|
"""
|
|
|
|
|
|
# BUG-14 FIX: Helper to safely escape strings for use in generated Python code
|
|
def _escape_for_python(value: str) -> str:
|
|
"""Escape a string so it can be safely embedded in generated Python source code.
|
|
Uses json.dumps which properly escapes quotes, backslashes, and special chars."""
|
|
return json.dumps(value)
|
|
|
|
|
|
def _env_example(chatbot: Dict, qdrant_url: str, qdrant_key: str):
|
|
name = chatbot.get("name", "My Chatbot").upper().replace(" ", "_")
|
|
return f"""# {name} - Environment Configuration
|
|
# Copy to .env and fill in your values
|
|
|
|
# LLM Provider (choose one)
|
|
LLM_PROVIDER=openai
|
|
LLM_MODEL=gpt-4o
|
|
LLM_API_KEY=sk-your-openai-key
|
|
|
|
# For Anthropic: sk-ant-your-key
|
|
# For Google: your-google-api-key
|
|
# For Fireworks: your-fireworks-key
|
|
|
|
# Embeddings (required - OpenAI)
|
|
EMBEDDING_API_KEY=sk-your-openai-key
|
|
EMBEDDING_MODEL=text-embedding-3-small
|
|
|
|
# Qdrant Vector Database
|
|
QDRANT_URL={qdrant_url}
|
|
QDRANT_API_KEY={qdrant_key}
|
|
QDRANT_COLLECTION={chatbot.get("qdrant_collection_name", "chatbot_collection")}
|
|
|
|
# Server
|
|
PORT=8000
|
|
HOST=0.0.0.0
|
|
ALLOWED_ORIGINS=http://localhost:3000,https://yourdomain.com
|
|
"""
|
|
|
|
|
|
def _main_py(chatbot: Dict):
|
|
# BUG-14 FIX: Use json.dumps to safely escape system_prompt
|
|
# Previously used f-string with triple quotes, which broke if prompt contained """ or {
|
|
safe_name = _escape_for_python(chatbot.get("name", "Chatbot"))
|
|
safe_prompt = _escape_for_python(chatbot.get("system_prompt") or "You are a helpful assistant.")
|
|
|
|
return f'''"""
|
|
Auto-generated FastAPI backend for: {chatbot.get("name", "Chatbot")}
|
|
Generated by Contexta Platform
|
|
"""
|
|
from fastapi import FastAPI, HTTPException
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from pydantic import BaseModel
|
|
from typing import List, Optional
|
|
import os
|
|
from dotenv import load_dotenv
|
|
from rag_engine import RAGEngine
|
|
|
|
load_dotenv()
|
|
|
|
# BUG-14 FIX: System prompt stored safely via json-escaped string
|
|
SYSTEM_PROMPT = {safe_prompt}
|
|
|
|
app = FastAPI(
|
|
title={safe_name} + " API",
|
|
version="1.0.0"
|
|
)
|
|
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=os.getenv("ALLOWED_ORIGINS", "*").split(","),
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
rag = RAGEngine(
|
|
qdrant_url=os.getenv("QDRANT_URL"),
|
|
qdrant_api_key=os.getenv("QDRANT_API_KEY"),
|
|
collection_name=os.getenv("QDRANT_COLLECTION"),
|
|
llm_provider=os.getenv("LLM_PROVIDER", "openai"),
|
|
llm_model=os.getenv("LLM_MODEL", "gpt-4o"),
|
|
llm_api_key=os.getenv("LLM_API_KEY"),
|
|
embedding_api_key=os.getenv("EMBEDDING_API_KEY"),
|
|
embedding_model=os.getenv("EMBEDDING_MODEL", "text-embedding-3-small"),
|
|
system_prompt=SYSTEM_PROMPT,
|
|
)
|
|
|
|
|
|
class ChatRequest(BaseModel):
|
|
message: str
|
|
session_id: Optional[str] = None
|
|
language: str = "en"
|
|
history: List[dict] = []
|
|
|
|
|
|
class Source(BaseModel):
|
|
document_name: str
|
|
text: str
|
|
score: float
|
|
|
|
|
|
class ChatResponse(BaseModel):
|
|
response: str
|
|
session_id: str
|
|
sources: List[Source]
|
|
|
|
|
|
@app.post("/chat", response_model=ChatResponse)
|
|
async def chat(request: ChatRequest):
|
|
import uuid
|
|
session_id = request.session_id or str(uuid.uuid4())
|
|
result = await rag.query(
|
|
query=request.message,
|
|
history=request.history,
|
|
language=request.language,
|
|
)
|
|
return ChatResponse(
|
|
response=result["response"],
|
|
session_id=session_id,
|
|
sources=[Source(**s) for s in result.get("sources", [])],
|
|
)
|
|
|
|
|
|
@app.get("/health")
|
|
def health():
|
|
return {{"status": "healthy", "chatbot": {safe_name}}}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import uvicorn
|
|
uvicorn.run(app, host=os.getenv("HOST", "0.0.0.0"), port=int(os.getenv("PORT", 8000)))
|
|
'''
|
|
|
|
|
|
def _rag_engine_py():
|
|
return '''"""RAG Engine - Retrieval-Augmented Generation"""
|
|
from qdrant_client import QdrantClient
|
|
from qdrant_client.http.models import Distance, VectorParams
|
|
from openai import AsyncOpenAI
|
|
from typing import List, Dict, Any, Optional
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class RAGEngine:
|
|
def __init__(self, qdrant_url, qdrant_api_key, collection_name,
|
|
llm_provider, llm_model, llm_api_key,
|
|
embedding_api_key, embedding_model, system_prompt):
|
|
self.collection_name = collection_name
|
|
self.llm_provider = llm_provider
|
|
self.llm_model = llm_model
|
|
self.llm_api_key = llm_api_key
|
|
self.system_prompt = system_prompt
|
|
self.embedding_model = embedding_model
|
|
|
|
# Initialize Qdrant
|
|
self.qdrant = QdrantClient(url=qdrant_url, api_key=qdrant_api_key)
|
|
|
|
# Initialize embedding client
|
|
self.embed_client = AsyncOpenAI(api_key=embedding_api_key)
|
|
|
|
async def _get_embedding(self, text: str) -> List[float]:
|
|
response = await self.embed_client.embeddings.create(
|
|
model=self.embedding_model,
|
|
input=text,
|
|
)
|
|
return response.data[0].embedding
|
|
|
|
async def _search_vectors(self, query_embedding: List[float], top_k: int = 5) -> List[Dict]:
|
|
results = self.qdrant.search(
|
|
collection_name=self.collection_name,
|
|
query_vector=query_embedding,
|
|
limit=top_k,
|
|
)
|
|
return [
|
|
{
|
|
"document_name": r.payload.get("document_name", "Unknown"),
|
|
"text": r.payload.get("text", ""),
|
|
"score": r.score,
|
|
}
|
|
for r in results
|
|
]
|
|
|
|
async def query(self, query: str, history: List[Dict] = None, language: str = "en") -> Dict:
|
|
# Get embedding for query
|
|
query_embedding = await self._get_embedding(query)
|
|
|
|
# Search for relevant chunks
|
|
sources = await self._search_vectors(query_embedding)
|
|
|
|
# Build context from sources
|
|
context = "\\n\\n".join([
|
|
f"[Source: {s['document_name']}]\\n{s['text']}"
|
|
for s in sources
|
|
])
|
|
|
|
# Build messages
|
|
messages = [
|
|
{"role": "system", "content": f"{self.system_prompt}\\n\\nUse the following context to answer:\\n{context}"},
|
|
]
|
|
|
|
if history:
|
|
messages.extend(history[-10:])
|
|
|
|
messages.append({"role": "user", "content": query})
|
|
|
|
# Generate response based on provider
|
|
response_text = await self._generate(messages)
|
|
|
|
return {
|
|
"response": response_text,
|
|
"sources": sources,
|
|
}
|
|
|
|
async def _generate(self, messages: List[Dict]) -> str:
|
|
if self.llm_provider == "openai":
|
|
from openai import AsyncOpenAI
|
|
client = AsyncOpenAI(api_key=self.llm_api_key)
|
|
response = await client.chat.completions.create(
|
|
model=self.llm_model,
|
|
messages=messages,
|
|
max_tokens=1000,
|
|
)
|
|
return response.choices[0].message.content
|
|
|
|
elif self.llm_provider == "anthropic":
|
|
import anthropic
|
|
client = anthropic.AsyncAnthropic(api_key=self.llm_api_key)
|
|
system = messages[0]["content"]
|
|
msgs = [m for m in messages[1:] if m["role"] in ("user", "assistant")]
|
|
response = await client.messages.create(
|
|
model=self.llm_model,
|
|
max_tokens=1000,
|
|
system=system,
|
|
messages=msgs,
|
|
)
|
|
return response.content[0].text
|
|
|
|
elif self.llm_provider == "google":
|
|
import google.generativeai as genai
|
|
genai.configure(api_key=self.llm_api_key)
|
|
model = genai.GenerativeModel(self.llm_model)
|
|
prompt = "\\n".join([f"{m['role']}: {m['content']}" for m in messages])
|
|
response = await model.generate_content_async(prompt)
|
|
return response.text
|
|
|
|
else:
|
|
import httpx
|
|
headers = {"Authorization": f"Bearer {self.llm_api_key}", "Content-Type": "application/json"}
|
|
async with httpx.AsyncClient(timeout=60) as client:
|
|
resp = await client.post(
|
|
"https://api.fireworks.ai/inference/v1/chat/completions",
|
|
headers=headers,
|
|
json={"model": self.llm_model, "messages": messages, "max_tokens": 1000},
|
|
)
|
|
resp.raise_for_status()
|
|
return resp.json()["choices"][0]["message"]["content"]
|
|
'''
|
|
|
|
|
|
def _dockerfile():
|
|
return """FROM python:3.11-slim
|
|
|
|
WORKDIR /app
|
|
COPY requirements.txt .
|
|
RUN pip install --no-cache-dir -r requirements.txt
|
|
COPY . .
|
|
|
|
EXPOSE 8000
|
|
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
|
"""
|
|
|
|
|
|
def _docker_compose(chatbot: Dict):
|
|
name = chatbot.get("name", "chatbot").lower().replace(" ", "-")
|
|
return f"""version: "3.8"
|
|
services:
|
|
api:
|
|
build: .
|
|
container_name: {name}-api
|
|
ports:
|
|
- "${{PORT:-8000}}:8000"
|
|
env_file:
|
|
- .env
|
|
restart: unless-stopped
|
|
"""
|
|
|
|
|
|
def _chat_widget_tsx(chatbot: Dict):
|
|
safe_name = json.dumps(chatbot.get("name", "Chatbot"))
|
|
safe_welcome = json.dumps(chatbot.get("welcome_message", "Hello! How can I help you?"))
|
|
color = chatbot.get("primary_color", "#6366f1")
|
|
return f'''import React, {{ useState }} from "react";
|
|
import {{ useChat }} from "./useChat";
|
|
|
|
export const ChatWidget: React.FC = () => {{
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
const {{ messages, isLoading, sendMessage }} = useChat({safe_welcome});
|
|
const [input, setInput] = useState("");
|
|
|
|
const handleSend = () => {{
|
|
if (!input.trim() || isLoading) return;
|
|
sendMessage(input.trim());
|
|
setInput("");
|
|
}};
|
|
|
|
return (
|
|
<>
|
|
{{isOpen && (
|
|
<div style={{{{ position: "fixed", bottom: 80, right: 20, width: 380, height: 500,
|
|
borderRadius: 16, overflow: "hidden", boxShadow: "0 8px 30px rgba(0,0,0,0.15)",
|
|
display: "flex", flexDirection: "column", background: "#fff", zIndex: 9999 }}}}>
|
|
<div style={{{{ background: "{color}", color: "#fff", padding: "12px 16px",
|
|
fontWeight: 600, fontSize: 14 }}}}>
|
|
{safe_name}
|
|
</div>
|
|
<div style={{{{ flex: 1, overflowY: "auto", padding: 12 }}}}>
|
|
{{messages.map((m, i) => (
|
|
<div key={{i}} style={{{{ display: "flex", justifyContent: m.role === "user" ? "flex-end" : "flex-start", marginBottom: 8 }}}}>
|
|
<div style={{{{ background: m.role === "user" ? "{color}" : "#f3f4f6",
|
|
color: m.role === "user" ? "#fff" : "#1f2937",
|
|
borderRadius: 12, padding: "8px 12px", maxWidth: "80%", fontSize: 13 }}}}>
|
|
{{m.content}}
|
|
</div>
|
|
</div>
|
|
))}}
|
|
{{isLoading && <div style={{{{ color: "#9ca3af", fontSize: 12 }}}}>Typing...</div>}}
|
|
</div>
|
|
<div style={{{{ borderTop: "1px solid #e5e7eb", padding: 8, display: "flex", gap: 8 }}}}>
|
|
<input value={{input}} onChange={{e => setInput(e.target.value)}}
|
|
onKeyDown={{e => e.key === "Enter" && handleSend()}}
|
|
placeholder="Type a message..." style={{{{ flex: 1, border: "1px solid #d1d5db",
|
|
borderRadius: 8, padding: "6px 10px", fontSize: 13, outline: "none" }}}} />
|
|
<button onClick={{handleSend}} disabled={{isLoading}}
|
|
style={{{{ background: "{color}", color: "#fff", border: "none", borderRadius: 8,
|
|
padding: "6px 14px", cursor: "pointer", fontSize: 13 }}}}>Send</button>
|
|
</div>
|
|
</div>
|
|
)}}
|
|
<button onClick={{() => setIsOpen(!isOpen)}} style={{{{ position: "fixed", bottom: 20,
|
|
right: 20, width: 56, height: 56, borderRadius: 28, background: "{color}",
|
|
color: "#fff", border: "none", cursor: "pointer", fontSize: 24, zIndex: 9999,
|
|
boxShadow: "0 4px 12px rgba(0,0,0,0.15)", display: "flex", alignItems: "center",
|
|
justifyContent: "center" }}}}>
|
|
{{isOpen ? "\\u00d7" : "\\ud83d\\udcac"}}
|
|
</button>
|
|
</>
|
|
);
|
|
}};
|
|
'''
|
|
|
|
|
|
def _use_chat_ts():
|
|
return '''import { useState, useCallback } from "react";
|
|
import { sendChatMessage } from "./api";
|
|
|
|
interface Message {
|
|
role: "user" | "assistant";
|
|
content: string;
|
|
}
|
|
|
|
export function useChat(welcomeMessage: string) {
|
|
const [messages, setMessages] = useState<Message[]>([
|
|
{ role: "assistant", content: welcomeMessage }
|
|
]);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [sessionId] = useState(() => crypto.randomUUID());
|
|
|
|
const sendMessage = useCallback(async (content: string) => {
|
|
setMessages(prev => [...prev, { role: "user", content }]);
|
|
setIsLoading(true);
|
|
try {
|
|
const history = messages.map(m => ({ role: m.role, content: m.content }));
|
|
const result = await sendChatMessage({ message: content, session_id: sessionId, history });
|
|
setMessages(prev => [...prev, { role: "assistant", content: result.response }]);
|
|
} catch {
|
|
setMessages(prev => [...prev, { role: "assistant", content: "Sorry, I encountered an error. Please try again." }]);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
}, [messages, sessionId]);
|
|
|
|
return { messages, isLoading, sendMessage };
|
|
}
|
|
'''
|
|
|
|
|
|
def _api_ts():
|
|
return '''const API_URL = import.meta.env.VITE_API_URL || "http://localhost:8000";
|
|
|
|
export async function sendChatMessage(payload: {
|
|
message: string;
|
|
session_id: string;
|
|
history?: any[];
|
|
}) {
|
|
const response = await fetch(`${API_URL}/chat`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(payload),
|
|
});
|
|
if (!response.ok) throw new Error("Chat request failed");
|
|
return response.json();
|
|
}
|
|
'''
|
|
|
|
|
|
def _types_ts():
|
|
return '''export interface Message {
|
|
role: "user" | "assistant";
|
|
content: string;
|
|
}
|
|
|
|
export interface Source {
|
|
document_name: string;
|
|
text: string;
|
|
score: number;
|
|
}
|
|
|
|
export interface ChatResponse {
|
|
response: string;
|
|
session_id: string;
|
|
sources: Source[];
|
|
}
|
|
'''
|
|
|
|
|
|
def _package_json(chatbot: Dict):
|
|
name = chatbot.get("name", "chatbot").lower().replace(" ", "-")
|
|
# Sanitize name for package.json
|
|
safe_name = "".join(c for c in name if c.isalnum() or c == "-")
|
|
return f'''{{
|
|
"name": "{safe_name}-widget",
|
|
"version": "1.0.0",
|
|
"scripts": {{
|
|
"dev": "vite",
|
|
"build": "tsc && vite build",
|
|
"preview": "vite preview"
|
|
}},
|
|
"dependencies": {{
|
|
"react": "^18.2.0",
|
|
"react-dom": "^18.2.0"
|
|
}},
|
|
"devDependencies": {{
|
|
"@types/react": "^18.2.0",
|
|
"@types/react-dom": "^18.2.0",
|
|
"typescript": "^5.0.0",
|
|
"vite": "^5.0.0",
|
|
"@vitejs/plugin-react": "^4.0.0"
|
|
}}
|
|
}}
|
|
'''
|
|
|
|
|
|
def _tsconfig():
|
|
return '''{
|
|
"compilerOptions": {
|
|
"target": "ES2020",
|
|
"lib": ["ES2020", "DOM"],
|
|
"module": "ESNext",
|
|
"moduleResolution": "bundler",
|
|
"jsx": "react-jsx",
|
|
"strict": true,
|
|
"esModuleInterop": true,
|
|
"skipLibCheck": true
|
|
}
|
|
}
|
|
'''
|
|
|
|
|
|
def _vite_config():
|
|
return '''import { defineConfig } from "vite";
|
|
import react from "@vitejs/plugin-react";
|
|
|
|
export default defineConfig({
|
|
plugins: [react()],
|
|
build: {
|
|
lib: {
|
|
entry: "src/main.tsx",
|
|
name: "ChatWidget",
|
|
fileName: "chatbot-widget"
|
|
},
|
|
rollupOptions: {
|
|
external: ["react", "react-dom"],
|
|
}
|
|
}
|
|
});
|
|
'''
|
|
|
|
|
|
def _backend_readme(chatbot: Dict):
|
|
return f"""# {chatbot.get("name", "Chatbot")} - Backend API
|
|
|
|
## Quick Start
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
# Edit .env with your API keys
|
|
pip install -r requirements.txt
|
|
uvicorn main:app --reload --port 8000
|
|
```
|
|
|
|
## Deploy with Docker
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
# Edit .env
|
|
docker-compose up -d
|
|
```
|
|
|
|
## API Endpoints
|
|
|
|
- `POST /chat` - Send a message
|
|
- `GET /health` - Health check
|
|
|
|
## Environment Variables
|
|
|
|
See `.env.example` for all required variables.
|
|
"""
|
|
|
|
|
|
def _frontend_readme(chatbot: Dict):
|
|
return f"""# {chatbot.get("name", "Chatbot")} - Chat Widget
|
|
|
|
## Quick Start
|
|
|
|
```bash
|
|
npm install
|
|
npm run dev
|
|
```
|
|
|
|
Create a `.env` file:
|
|
```
|
|
VITE_API_URL=http://localhost:8000
|
|
```
|
|
|
|
## Build for Production
|
|
|
|
```bash
|
|
npm run build
|
|
```
|
|
|
|
The built files will be in `dist/`.
|
|
|
|
## Embed in Your Website
|
|
|
|
```html
|
|
<script src="path/to/dist/chatbot-widget.js"></script>
|
|
```
|
|
"""
|
|
|
|
|
|
def _quick_start(chatbot: Dict):
|
|
return f"""# {chatbot.get("name", "Chatbot")} - Quick Start Guide
|
|
|
|
## Prerequisites
|
|
|
|
- Python 3.11+
|
|
- Node.js 18+ (for the widget)
|
|
|
|
## Step 1: Backend Setup (2 minutes)
|
|
|
|
```bash
|
|
cd backend
|
|
cp .env.example .env
|
|
# Edit .env with your API keys
|
|
pip install -r requirements.txt
|
|
uvicorn main:app --reload
|
|
```
|
|
|
|
Visit http://localhost:8000/health to verify.
|
|
|
|
## Step 2: Frontend Setup (1 minute)
|
|
|
|
```bash
|
|
cd frontend
|
|
npm install
|
|
echo "VITE_API_URL=http://localhost:8000" > .env
|
|
npm run dev
|
|
```
|
|
|
|
## Step 3: Test It!
|
|
|
|
Open http://localhost:5173 and start chatting!
|
|
|
|
## Deployment
|
|
|
|
See `backend/README.md` and `frontend/README.md` for deployment guides.
|
|
"""
|
|
|
|
|
|
def _setup_wizard(chatbot: Dict):
|
|
return f'''#!/usr/bin/env python3
|
|
"""Interactive setup wizard for {chatbot.get("name", "Chatbot")}"""
|
|
import os
|
|
import sys
|
|
|
|
def main():
|
|
print("=" * 50)
|
|
print(f" Setup Wizard: {chatbot.get("name", "Chatbot")}")
|
|
print("=" * 50)
|
|
print()
|
|
|
|
env_vars = {{}}
|
|
|
|
# LLM Provider
|
|
print("Choose your LLM provider:")
|
|
print(" 1. OpenAI (GPT-4o, GPT-4 Turbo)")
|
|
print(" 2. Anthropic (Claude 3.5 Sonnet)")
|
|
print(" 3. Google (Gemini 1.5 Pro)")
|
|
print(" 4. Fireworks AI (Llama, Mixtral)")
|
|
choice = input("Enter choice [1]: ").strip() or "1"
|
|
|
|
providers = {{"1": "openai", "2": "anthropic", "3": "google", "4": "fireworks"}}
|
|
env_vars["LLM_PROVIDER"] = providers.get(choice, "openai")
|
|
|
|
env_vars["LLM_API_KEY"] = input(f"Enter {{env_vars['LLM_PROVIDER']}} API key: ").strip()
|
|
env_vars["EMBEDDING_API_KEY"] = input("Enter OpenAI API key (for embeddings): ").strip() or env_vars["LLM_API_KEY"]
|
|
|
|
# Write .env
|
|
env_path = os.path.join("backend", ".env")
|
|
with open(env_path, "w") as f:
|
|
for k, v in env_vars.items():
|
|
f.write(f"{{k}}={{v}}\\n")
|
|
|
|
print(f"\\nConfiguration saved to {{env_path}}")
|
|
print("\\nTo start: cd backend && uvicorn main:app --reload")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
''' |