|
from __future__ import annotations
|
|
from typing import AsyncIterable, List, Dict
|
|
import requests
|
|
from fastapi_poe import PoeBot, QueryRequest, PartialResponse, SettingsRequest, SettingsResponse, make_app
|
|
from fastapi import Header, HTTPException
|
|
from modal import Image, App, asgi_app
|
|
import os
|
|
|
|
|
|
NGROK_URL = "https://fca7-2601-2c1-280-1320-b881-aac7-9186-9365.ngrok-free.app"
|
|
LM_STUDIO_CHAT_URL = f"{NGROK_URL}/api/v0/chat/completions"
|
|
LM_STUDIO_EMBEDDINGS_URL = f"{NGROK_URL}/api/v0/embeddings"
|
|
|
|
|
|
MODEL_NAME = "bartowski/Qwen2.5-Coder-32B-Instruct-GGUF/Qwen2.5-Coder-32B-Instruct-IQ2_M.gguf"
|
|
|
|
|
|
NEW_BOT_ACCESS_KEY = "Kskn3sHdJh7nkVWGcpwcGOIHYSrsML3l"
|
|
|
|
|
|
user_histories: Dict[str, List[dict]] = {}
|
|
|
|
class main(PoeBot):
|
|
async def get_response(
|
|
self, request: QueryRequest, authorization: str = Header(...)
|
|
) -> AsyncIterable[PartialResponse]:
|
|
"""
|
|
Handle user queries dynamically while validating the Poe access key.
|
|
"""
|
|
|
|
if authorization != NEW_BOT_ACCESS_KEY:
|
|
print("Warning: Unauthorized access key used.")
|
|
|
|
|
|
|
|
user_id = request.user_id if hasattr(request, "user_id") else str(hash(request))
|
|
if not user_id:
|
|
yield PartialResponse(text="Error: User identifier not provided.")
|
|
return
|
|
|
|
|
|
user_message = request.query[-1].content
|
|
|
|
|
|
if user_id not in user_histories:
|
|
user_histories[user_id] = []
|
|
conversation_history = user_histories[user_id]
|
|
|
|
try:
|
|
|
|
conversation_history.append({"role": "user", "content": user_message})
|
|
|
|
|
|
response_text = self.get_chat_completion(conversation_history)
|
|
|
|
|
|
conversation_history.append({"role": "assistant", "content": response_text})
|
|
|
|
|
|
yield PartialResponse(text=response_text.strip())
|
|
except Exception as e:
|
|
|
|
yield PartialResponse(text=f"An error occurred: {e}")
|
|
|
|
def get_chat_completion(self, messages: List[dict]) -> str:
|
|
"""
|
|
Send a chat completion request to LM Studio's /api/v0/chat/completions endpoint.
|
|
"""
|
|
payload = {
|
|
"model": MODEL_NAME,
|
|
"messages": messages,
|
|
"temperature": 0.7,
|
|
"max_tokens": 1024,
|
|
"stream": False
|
|
}
|
|
response = requests.post(LM_STUDIO_CHAT_URL, json=payload, timeout=30)
|
|
response.raise_for_status()
|
|
generated_text = response.json().get("choices", [{}])[0].get("message", {}).get("content", "")
|
|
|
|
|
|
if not generated_text:
|
|
generated_text = "I'm sorry, I couldn't generate a response. Could you please try again?"
|
|
|
|
return generated_text
|
|
|
|
def get_embeddings(self, text: str) -> str:
|
|
"""
|
|
Generate text embeddings using LM Studio's /api/v0/embeddings endpoint.
|
|
"""
|
|
payload = {"model": "openai/clip-vit-base-patch32", "input": text}
|
|
response = requests.post(LM_STUDIO_EMBEDDINGS_URL, json=payload, timeout=10)
|
|
response.raise_for_status()
|
|
embedding = response.json().get("data", [{}])[0].get("embedding", [])
|
|
return f"Embeddings: {embedding}"
|
|
|
|
async def get_settings(self, setting: SettingsRequest) -> SettingsResponse:
|
|
"""
|
|
Configure the bot's capabilities for Poe, such as enabling attachments.
|
|
"""
|
|
return SettingsResponse(
|
|
allow_attachments=False,
|
|
)
|
|
|
|
|
|
REQUIREMENTS = ["fastapi-poe==0.0.24", "requests==2.31.0"]
|
|
image = Image.debian_slim().pip_install(REQUIREMENTS)
|
|
new_app = App("another-secure-lmstudio-poe-bot")
|
|
|
|
@new_app.function(image=image)
|
|
@asgi_app()
|
|
def fastapi_app():
|
|
bot = AnotherSecureLMStudioBot()
|
|
return make_app(bot, allow_without_key=True)
|
|
|