Spaces:
Running
Running
from typing import List, Dict | |
from abc import ABC, abstractmethod | |
from openai import OpenAI | |
import logging | |
import httpx | |
class LLMInterface(ABC): | |
def generate(self, prompt: str) -> str: | |
pass | |
class DeepseekInterface(LLMInterface): | |
def __init__(self, api_key: str, base_url: str, model: str): | |
self.api_key = api_key | |
self.base_url = base_url | |
self.model = model | |
self.headers = { | |
"Authorization": f"Bearer {api_key}", | |
"Content-Type": "application/json" | |
} | |
def _build_system_prompt(self, role: str) -> str: | |
"""构建系统提示词""" | |
roles = { | |
"summarizer": "You are a professional tourism content analyst, good at extracting and summarizing key tourism-related information. Please answer in English", | |
"planner": "You are a professional travel planner who is good at making detailed travel plans. Please answer in English" | |
} | |
return roles.get(role, "You are a professional AI assistant. Please answer in English") | |
def generate(self, prompt: str, role: str = "planner") -> str: | |
import requests | |
from requests.adapters import HTTPAdapter | |
from urllib3.util.retry import Retry | |
session = requests.Session() | |
retries = Retry( | |
total=3, | |
backoff_factor=1, | |
status_forcelist=[500, 502, 503, 504] | |
) | |
session.mount('https://', HTTPAdapter(max_retries=retries)) | |
payload = { | |
"model": self.model, | |
"messages": [ | |
{ | |
"role": "system", | |
"content": self._build_system_prompt(role) | |
}, | |
{ | |
"role": "user", | |
"content": prompt | |
} | |
], | |
"temperature": 0.7, | |
"max_tokens": 2000 | |
} | |
try: | |
response = session.post( | |
f"{self.base_url}/v1/chat/completions", | |
headers=self.headers, | |
json=payload, | |
timeout=(10, 60) | |
) | |
response.raise_for_status() | |
return response.json()['choices'][0]['message']['content'] | |
except requests.exceptions.Timeout: | |
print("Deepseek API request timeout, retrying...") | |
return "Sorry, due to network issues, content generation is temporarily unavailable. Please try again later." | |
except requests.exceptions.RequestException as e: | |
print(f"Error calling Deepseek API: {str(e)}") | |
return "Sorry, an error occurred while generating content. Please try again later." | |
def summarize_document(self, content: str, title: str, url: str) -> str: | |
"""使用 Deepseek 总结文档""" | |
prompt = f"""Please analyze the following tourism web content and generate a rich summary paragraph. | |
Web Title: {title} | |
Web Link: {url} | |
Web Content: | |
{content[:4000]} | |
Requirements: | |
1. The summary should be between 300-500 words | |
2. Keep the most important tourism information (attractions, suggestions, tips, etc.) | |
3. Use an objective tone | |
4. Information should be accurate and practical | |
5. Remove marketing and advertising content | |
6. Maintain logical coherence | |
Please return the summary content directly, without any other explanation.""" | |
return self.generate(prompt, role="summarizer") | |
def generate_travel_plan(self, query: str, context: List[Dict]) -> str: | |
# 构建更结构化的上下文 | |
context_text = "\n\n".join([ | |
f"Source {i+1} ({doc.get('title', 'Unknown Title')}):\n{doc['passage']}" | |
for i, doc in enumerate(context) | |
]) | |
prompt = f"""As a professional travel planner, please create a detailed travel plan based on the user's needs and reference materials. | |
User Needs: {query} | |
Reference Materials: | |
{context_text} | |
Please provide the following content: | |
1. Itinerary Overview (Overall arrangement and key attractions) | |
2. Daily detailed itinerary (includes specific time, location, and transportation methods) | |
3. Traffic suggestions (includes practical APP recommendations) | |
4. Accommodation recommendations (includes specific areas and hotel suggestions) | |
5. Food recommendations (includes specialty restaurants and snacks) | |
6. Practical tips (weather, clothing, essential items, etc.) | |
Requirements: | |
1. The itinerary should be reasonable, considering the distance between attractions | |
2. Provide specific time points | |
3. Include detailed traffic guidance | |
4. Suggestions should be specific and practical | |
5. Consider actual conditions (e.g., opening hours of attractions) | |
Please return the travel plan content directly, without any other explanation.""" | |
return self.generate(prompt, role="planner") | |
class OllamaInterface(LLMInterface): | |
def __init__(self, base_url: str, model: str): | |
self.base_url = base_url.rstrip('/') | |
self.model = model | |
self.headers = { | |
"Content-Type": "application/json" | |
} | |
def _build_system_prompt(self, role: str) -> str: | |
"""构建系统提示词""" | |
roles = { | |
"summarizer": "You are a professional tourism content analyst, good at extracting and summarizing key tourism-related information. Please answer in English", | |
"planner": "You are a professional travel planner who is good at making detailed travel plans. Please answer in English" | |
} | |
return roles.get(role, "You are a professional AI assistant. Please answer in English") | |
def generate(self, prompt: str, role: str = "planner") -> str: | |
import requests | |
payload = { | |
"model": self.model, | |
"messages": [ | |
{ | |
"role": "system", | |
"content": self._build_system_prompt(role) | |
}, | |
{ | |
"role": "user", | |
"content": prompt | |
} | |
], | |
"stream": False | |
} | |
try: | |
response = requests.post( | |
f"{self.base_url}/api/chat", | |
headers=self.headers, | |
json=payload, | |
timeout=(10, 60) | |
) | |
response.raise_for_status() | |
return response.json()['message']['content'] | |
except Exception as e: | |
print(f"Error calling Ollama API: {str(e)}") | |
return "Sorry, an error occurred while generating content. Please try again later." | |
def summarize_document(self, content: str, title: str, url: str) -> str: | |
"""使用 Ollama 总结文档""" | |
prompt = f"""Please analyze the following tourism web content and generate a rich summary paragraph. | |
Web Title: {title} | |
Web Link: {url} | |
Web Content: | |
{content[:4000]} | |
Requirements: | |
1. The summary should be between 300-500 words | |
2. Keep the most important tourism information (attractions, suggestions, tips, etc.) | |
3. Use an objective tone | |
4. Information should be accurate and practical | |
5. Remove marketing and advertising content | |
6. Maintain logical coherence | |
Please return the summary content directly, without any other explanation.""" | |
return self.generate(prompt, role="summarizer") | |
def generate_travel_plan(self, query: str, context: List[Dict]) -> str: | |
# 构建更结构化的上下文 | |
context_text = "\n\n".join([ | |
f"来源 {i+1} ({doc.get('title', '未知标题')}):\n{doc['passage']}" | |
for i, doc in enumerate(context) | |
]) | |
prompt = f"""As a professional travel planner, please create a detailed travel plan based on the user's needs and reference materials. | |
User Needs: {query} | |
Reference Materials: | |
{context_text} | |
Please provide the following content: | |
1. Itinerary Overview (Overall arrangement and key attractions) | |
2. Daily detailed itinerary (includes specific time, location, and transportation methods) | |
3. Traffic suggestions (includes practical APP recommendations) | |
4. Accommodation recommendations (includes specific areas and hotel suggestions) | |
5. Food recommendations (includes specialty restaurants and snacks) | |
6. Practical tips (weather, clothing, essential items, etc.) | |
Requirements: | |
1. The itinerary should be reasonable, considering the distance between attractions | |
2. Provide specific time points | |
3. Include detailed traffic guidance | |
4. Suggestions should be specific and practical | |
5. Consider actual conditions (e.g., opening hours of attractions) | |
Please return the travel plan content directly, without any other explanation.""" | |
return self.generate(prompt, role="planner") | |
class OpenAIInterface(LLMInterface): | |
def __init__(self, api_key: str, model: str = "gpt-4o", base_url: str = "https://api.feidaapi.com/v1"): | |
self.api_key = api_key | |
self.model = model | |
self.client = OpenAI(api_key=api_key, base_url=base_url) | |
def _build_system_prompt(self, role: str) -> str: | |
"""构建系统提示词""" | |
roles = { | |
"summarizer": "You are a professional tourism content analyst, good at extracting and summarizing key tourism-related information. Please answer in English", | |
"planner": "You are a professional travel planner who is good at making detailed travel plans. Please answer in English" | |
} | |
return roles.get(role, "You are a professional AI assistant. Please answer in English") | |
def generate(self, prompt: str, role: str = "planner") -> str: | |
try: | |
messages = [ | |
{"role": "system", "content": self._build_system_prompt(role)}, | |
{"role": "user", "content": prompt} | |
] | |
response = self.client.chat.completions.create( | |
model=self.model, | |
messages=messages, | |
temperature=0.7, | |
max_tokens=2000 | |
) | |
return response.choices[0].message.content | |
except Exception as e: | |
logging.error(f"Error calling OpenAI API: {str(e)}") | |
return "Sorry, an error occurred while generating content. Please try again later." | |
def summarize_document(self, content: str, title: str, url: str) -> str: | |
"""使用 OpenAI 总结文档""" | |
prompt = f"""Please analyze the following tourism web content and generate a rich summary paragraph. | |
Web Title: {title} | |
Web Link: {url} | |
Web Content: | |
{content[:4000]} | |
Requirements: | |
1. The summary should be between 300-500 words | |
2. Keep the most important tourism information (attractions, suggestions, tips, etc.) | |
3. Use an objective tone | |
4. Information should be accurate and practical | |
5. Remove marketing and advertising content | |
6. Maintain logical coherence | |
Please return the summary content directly, without any other explanation.""" | |
return self.generate(prompt, role="summarizer") | |
def generate_travel_plan(self, query: str, context: List[Dict]) -> str: | |
# 构建更结构化的上下文 | |
context_text = "\n\n".join([ | |
f"Source {i+1} ({doc.get('title', 'Unknown Title')}):\n{doc['passage']}" | |
for i, doc in enumerate(context) | |
]) | |
prompt = f"""As a professional travel planner, please create a detailed travel plan based on the user's needs and reference materials. | |
User Needs: {query} | |
Reference Materials: | |
{context_text} | |
Please provide the following content: | |
1. Itinerary Overview (Overall arrangement and key attractions) | |
2. Daily detailed itinerary (includes specific time, location, and transportation methods) | |
3. Traffic suggestions (includes practical APP recommendations) | |
4. Accommodation recommendations (includes specific areas and hotel suggestions) | |
5. Food recommendations (includes specialty restaurants and snacks) | |
6. Practical tips (weather, clothing, essential items, etc.) | |
Requirements: | |
1. The itinerary should be reasonable, considering the distance between attractions | |
2. Provide specific time points | |
3. Include detailed traffic guidance | |
4. Suggestions should be specific and practical | |
5. Consider actual conditions (e.g., opening hours of attractions) | |
Please return the travel plan content directly, without any other explanation.""" | |
return self.generate(prompt, role="planner") |