|
@@ -0,0 +1,445 @@
|
|
|
|
|
+from fastapi import FastAPI, HTTPException, Depends, Request
|
|
|
|
|
+from fastapi.responses import JSONResponse
|
|
|
|
|
+from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
|
+from typing import List, Dict, Any
|
|
|
|
|
+import json
|
|
|
|
|
+import httpx
|
|
|
|
|
+from models import *
|
|
|
|
|
+from auth import *
|
|
|
|
|
+
|
|
|
|
|
+app = FastAPI(title="Recommendation Test API")
|
|
|
|
|
+
|
|
|
|
|
+# CORS
|
|
|
|
|
+app.add_middleware(
|
|
|
|
|
+ CORSMiddleware,
|
|
|
|
|
+ allow_origins=["*"],
|
|
|
|
|
+ allow_credentials=True,
|
|
|
|
|
+ allow_methods=["*"],
|
|
|
|
|
+ allow_headers=["*"],
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+# In-memory storage
|
|
|
|
|
+users_db = {}
|
|
|
|
|
+user_sessions = {}
|
|
|
|
|
+user_answers_db = {}
|
|
|
|
|
+
|
|
|
|
|
+# LLM configuration
|
|
|
|
|
+LLM_API_URL = "https://api.openai.com/v1/chat/completions"
|
|
|
|
|
+LLM_API_KEY = "your-llm-api-key"
|
|
|
|
|
+# Словарь форм предпринимательства с описаниями
|
|
|
|
|
+BUSINESS_FORMS = {
|
|
|
|
|
+ "ИП": {
|
|
|
|
|
+ "name": "Индивидуальный предприниматель (ИП)",
|
|
|
|
|
+ "description": "Самая популярная форма для малого бизнеса. Простая регистрация, минимальная отчетность, но полная ответственность по обязательствам.",
|
|
|
|
|
+ "advantages": [
|
|
|
|
|
+ "Простота регистрации и закрытия",
|
|
|
|
|
+ "Минимальная отчетность",
|
|
|
|
|
+ "Свободное распоряжение доходами",
|
|
|
|
|
+ "Низкие налоги при УСН"
|
|
|
|
|
+ ],
|
|
|
|
|
+ "disadvantages": [
|
|
|
|
|
+ "Ответственность всем личным имуществом",
|
|
|
|
|
+ "Ограничения на некоторые виды деятельности",
|
|
|
|
|
+ "Не подходит для привлечения инвесторов"
|
|
|
|
|
+ ],
|
|
|
|
|
+ "taxes": "УСН (6% с доходов или 15% с прибыли), Патент, НПД",
|
|
|
|
|
+ "liability": "Полная всем имуществом",
|
|
|
|
|
+ "registration_cost": "800-5000 руб.",
|
|
|
|
|
+ "best_for": "Небольшой бизнес, фриланс, розничная торговля"
|
|
|
|
|
+ },
|
|
|
|
|
+ "ООО": {
|
|
|
|
|
+ "name": "Общество с ограниченной ответственностью",
|
|
|
|
|
+ "description": "Юридическое лицо, идеально подходящее для бизнеса с партнерами. Ответственность ограничена уставным капиталом.",
|
|
|
|
|
+ "advantages": [
|
|
|
|
|
+ "Ответственность в пределах уставного капитала",
|
|
|
|
|
+ "Возможность привлечения партнеров",
|
|
|
|
|
+ "Больше доверия у контрагентов",
|
|
|
|
|
+ "Нет ограничений по видам деятельности"
|
|
|
|
|
+ ],
|
|
|
|
|
+ "disadvantages": [
|
|
|
|
|
+ "Сложнее отчетность",
|
|
|
|
|
+ "Высокие штрафы за нарушения",
|
|
|
|
|
+ "Налог на прибыль 20%",
|
|
|
|
|
+ "Сложнее закрыть"
|
|
|
|
|
+ ],
|
|
|
|
|
+ "taxes": "УСН, ОСНО (20% прибыль + НДС)",
|
|
|
|
|
+ "liability": "В пределах уставного капитала (от 10 тыс. руб.)",
|
|
|
|
|
+ "registration_cost": "4000-15000 руб.",
|
|
|
|
|
+ "best_for": "Бизнес с партнерами, средний бизнес, торговля с юрлицами"
|
|
|
|
|
+ },
|
|
|
|
|
+ "АО": {
|
|
|
|
|
+ "name": "Акционерное общество",
|
|
|
|
|
+ "description": "Форма для крупного бизнеса с возможностью привлечения инвестиций через продажу акций.",
|
|
|
|
|
+ "advantages": [
|
|
|
|
|
+ "Возможность привлечения крупных инвестиций",
|
|
|
|
|
+ "Высокий статус и доверие",
|
|
|
|
|
+ "Ответственность только стоимостью акций",
|
|
|
|
|
+ "Свободная продажа акций"
|
|
|
|
|
+ ],
|
|
|
|
|
+ "disadvantages": [
|
|
|
|
|
+ "Сложная регистрация и отчетность",
|
|
|
|
|
+ "Публичное раскрытие информации",
|
|
|
|
|
+ "Высокие затраты на содержание",
|
|
|
|
|
+ "Строгие требования законодательства"
|
|
|
|
|
+ ],
|
|
|
|
|
+ "taxes": "ОСНО (20% прибыль + НДС)",
|
|
|
|
|
+ "liability": "В пределах стоимости акций",
|
|
|
|
|
+ "registration_cost": "от 50000 руб.",
|
|
|
|
|
+ "best_for": "Крупный бизнес, привлечение инвестиций, выход на биржу"
|
|
|
|
|
+ },
|
|
|
|
|
+ "Самозанятость": {
|
|
|
|
|
+ "name": "Самозанятость (НПД)",
|
|
|
|
|
+ "description": "Упрощенная система для небольших доходов без найма сотрудников. Максимально простая отчетность через приложение.",
|
|
|
|
|
+ "advantages": [
|
|
|
|
|
+ "Нет отчетности (кроме чеков)",
|
|
|
|
|
+ "Налог всего 4-6%",
|
|
|
|
|
+ "Регистрация через приложение",
|
|
|
|
|
+ "Никаких взносов"
|
|
|
|
|
+ ],
|
|
|
|
|
+ "disadvantages": [
|
|
|
|
|
+ "Лимит дохода 2.4 млн руб. в год",
|
|
|
|
|
+ "Нельзя нанимать сотрудников",
|
|
|
|
|
+ "Ограничения по видам деятельности",
|
|
|
|
|
+ "Не подходит для бизнеса с юрлицами"
|
|
|
|
|
+ ],
|
|
|
|
|
+ "taxes": "НПД 4% (с физлиц) или 6% (с юрлиц)",
|
|
|
|
|
+ "liability": "Минимальная",
|
|
|
|
|
+ "registration_cost": "Бесплатно",
|
|
|
|
|
+ "best_for": "Фриланс, репетиторство, небольшие услуги"
|
|
|
|
|
+ },
|
|
|
|
|
+ "КФХ": {
|
|
|
|
|
+ "name": "Крестьянское фермерское хозяйство",
|
|
|
|
|
+ "description": "Специальная форма для сельскохозяйственной деятельности с льготным налогообложением.",
|
|
|
|
|
+ "advantages": [
|
|
|
|
|
+ "Льготные налоги (ЕСХН 6%)",
|
|
|
|
|
+ "Господдержка и субсидии",
|
|
|
|
|
+ "Проще отчетность чем у ООО",
|
|
|
|
|
+ "Можно без образования юрлица"
|
|
|
|
|
+ ],
|
|
|
|
|
+ "disadvantages": [
|
|
|
|
|
+ "Только для сельского хозяйства",
|
|
|
|
|
+ "Сезонные риски",
|
|
|
|
|
+ "Зависимость от погодных условий",
|
|
|
|
|
+ "Специфические требования"
|
|
|
|
|
+ ],
|
|
|
|
|
+ "taxes": "ЕСХН (6% с прибыли)",
|
|
|
|
|
+ "liability": "Зависит от формы регистрации",
|
|
|
|
|
+ "registration_cost": "1000-5000 руб.",
|
|
|
|
|
+ "best_for": "Сельское хозяйство, фермерство, животноводство"
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+# Загрузка вопросов
|
|
|
|
|
+try:
|
|
|
|
|
+ with open('questions.json', 'r', encoding='utf-8') as f:
|
|
|
|
|
+ questions = json.load(f)
|
|
|
|
|
+except FileNotFoundError:
|
|
|
|
|
+ questions = [
|
|
|
|
|
+ {
|
|
|
|
|
+ "id": 1,
|
|
|
|
|
+ "text": "Какой вид отдыха вы предпочитаете?",
|
|
|
|
|
+ "ans_1": "Активный туризм",
|
|
|
|
|
+ "ans_2": "Пляжный отдых",
|
|
|
|
|
+ "ans_3": "Экскурсии по городам",
|
|
|
|
|
+ "ans_4": "Горнолыжный курорт",
|
|
|
|
|
+ "vals": [3, 1, 2, 4]
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+
|
|
|
|
|
+# Корневой эндпоинт
|
|
|
|
|
+@app.get("/")
|
|
|
|
|
+async def root():
|
|
|
|
|
+ return {"message": "Recommendation Test API", "status": "running"}
|
|
|
|
|
+
|
|
|
|
|
+# Эндпоинт для favicon
|
|
|
|
|
+@app.get("/favicon.ico")
|
|
|
|
|
+async def favicon():
|
|
|
|
|
+ return JSONResponse(content={}, status_code=200)
|
|
|
|
|
+
|
|
|
|
|
+# 1. Auth endpoint
|
|
|
|
|
+@app.post("/auth/register")
|
|
|
|
|
+async def register(user: UserCreate):
|
|
|
|
|
+ if user.username in users_db:
|
|
|
|
|
+ raise HTTPException(status_code=400, detail="Username already exists")
|
|
|
|
|
+
|
|
|
|
|
+ hashed_password = get_password_hash(user.password)
|
|
|
|
|
+ users_db[user.username] = {
|
|
|
|
|
+ "username": user.username,
|
|
|
|
|
+ "hashed_password": hashed_password
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ # Инициализируем пустые ответы для пользователя
|
|
|
|
|
+ user_answers_db[user.username] = []
|
|
|
|
|
+
|
|
|
|
|
+ access_token = create_access_token(data={"sub": user.username})
|
|
|
|
|
+ return {"access_token": access_token, "token_type": "bearer"}
|
|
|
|
|
+
|
|
|
|
|
+@app.post("/auth/login")
|
|
|
|
|
+async def login(user: UserLogin):
|
|
|
|
|
+ user_data = users_db.get(user.username)
|
|
|
|
|
+ if not user_data or not verify_password(user.password, user_data["hashed_password"]):
|
|
|
|
|
+ raise HTTPException(status_code=401, detail="Invalid credentials")
|
|
|
|
|
+
|
|
|
|
|
+ # Убедимся, что у пользователя есть запись для ответов
|
|
|
|
|
+ if user.username not in user_answers_db:
|
|
|
|
|
+ user_answers_db[user.username] = []
|
|
|
|
|
+
|
|
|
|
|
+ access_token = create_access_token(data={"sub": user.username})
|
|
|
|
|
+ return {"access_token": access_token, "token_type": "bearer"}
|
|
|
|
|
+
|
|
|
|
|
+# 2. Получить все вопросы
|
|
|
|
|
+@app.get("/questions")
|
|
|
|
|
+async def get_all_questions(current_user: dict = Depends(get_current_user)):
|
|
|
|
|
+ # Возвращаем вопросы без значений vals
|
|
|
|
|
+ formatted_questions = []
|
|
|
|
|
+ for q in questions:
|
|
|
|
|
+ formatted_questions.append({
|
|
|
|
|
+ "id": q["id"],
|
|
|
|
|
+ "text": q["text"],
|
|
|
|
|
+ "answers": [
|
|
|
|
|
+ q["ans_1"],
|
|
|
|
|
+ q["ans_2"],
|
|
|
|
|
+ q["ans_3"],
|
|
|
|
|
+ q["ans_4"]
|
|
|
|
|
+ ]
|
|
|
|
|
+ })
|
|
|
|
|
+ return formatted_questions
|
|
|
|
|
+
|
|
|
|
|
+# 3. Получить конкретный вопрос
|
|
|
|
|
+@app.get("/questions/{question_id}")
|
|
|
|
|
+async def get_question(question_id: int, current_user: dict = Depends(get_current_user)):
|
|
|
|
|
+ question = next((q for q in questions if q["id"] == question_id), None)
|
|
|
|
|
+ if not question:
|
|
|
|
|
+ raise HTTPException(status_code=404, detail="Question not found")
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ "id": question["id"],
|
|
|
|
|
+ "text": question["text"],
|
|
|
|
|
+ "answers": [
|
|
|
|
|
+ question["ans_1"],
|
|
|
|
|
+ question["ans_2"],
|
|
|
|
|
+ question["ans_3"],
|
|
|
|
|
+ question["ans_4"]
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+# 4. Сохранить ответ
|
|
|
|
|
+@app.post("/answers")
|
|
|
|
|
+async def save_answers(answers: UserAnswers, current_user: dict = Depends(get_current_user)):
|
|
|
|
|
+ username = current_user["sub"]
|
|
|
|
|
+
|
|
|
|
|
+ if username not in user_answers_db:
|
|
|
|
|
+ user_answers_db[username] = []
|
|
|
|
|
+
|
|
|
|
|
+ user_answers_db[username].extend(answers.answers)
|
|
|
|
|
+ return {"message": "Answers saved successfully", "answers_count": len(user_answers_db[username])}
|
|
|
|
|
+
|
|
|
|
|
+# 5. Сброс прогресса
|
|
|
|
|
+@app.post("/reset")
|
|
|
|
|
+async def reset_progress(current_user: dict = Depends(get_current_user)):
|
|
|
|
|
+ username = current_user["sub"]
|
|
|
|
|
+ user_answers_db[username] = []
|
|
|
|
|
+ return {"message": "Progress reset successfully"}
|
|
|
|
|
+
|
|
|
|
|
+# 6. Получить результаты
|
|
|
|
|
+@app.get("/results")
|
|
|
|
|
+async def get_results(current_user: dict = Depends(get_current_user)):
|
|
|
|
|
+ username = current_user["sub"]
|
|
|
|
|
+ user_answers = user_answers_db.get(username, [])
|
|
|
|
|
+
|
|
|
|
|
+ if not user_answers:
|
|
|
|
|
+ raise HTTPException(status_code=400, detail="No answers found")
|
|
|
|
|
+
|
|
|
|
|
+ # Подсчет баллов для каждой формы предпринимательства
|
|
|
|
|
+ scores = {form: 0 for form in BUSINESS_FORMS.keys()}
|
|
|
|
|
+
|
|
|
|
|
+ for answer in user_answers:
|
|
|
|
|
+ question = next((q for q in questions if q["id"] == answer.question_id), None)
|
|
|
|
|
+ if question and answer.answer_index < len(question["business_forms"]):
|
|
|
|
|
+ selected_form = question["business_forms"][answer.answer_index]
|
|
|
|
|
+ if selected_form in scores:
|
|
|
|
|
+ scores[selected_form] += 1
|
|
|
|
|
+
|
|
|
|
|
+ # Сортируем по количеству баллов и берем топ-3
|
|
|
|
|
+ top_results = sorted(scores.items(), key=lambda x: x[1], reverse=True)[:3]
|
|
|
|
|
+
|
|
|
|
|
+ # Форматируем результаты с полной информацией
|
|
|
|
|
+ formatted_results = []
|
|
|
|
|
+ for form_id, score in top_results:
|
|
|
|
|
+ if form_id in BUSINESS_FORMS:
|
|
|
|
|
+ form_info = BUSINESS_FORMS[form_id].copy()
|
|
|
|
|
+ form_info["id"] = form_id
|
|
|
|
|
+ form_info["match_score"] = score
|
|
|
|
|
+ form_info["match_percentage"] = int((score / len(user_answers)) * 100)
|
|
|
|
|
+ formatted_results.append(form_info)
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ "results": formatted_results,
|
|
|
|
|
+ "total_questions": len(questions),
|
|
|
|
|
+ "answered_questions": len(user_answers)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+# 7. Получить детали результата
|
|
|
|
|
+@app.post("/details")
|
|
|
|
|
+async def get_result_details(request: ResultRequest, current_user: dict = Depends(get_current_user)):
|
|
|
|
|
+ form_id = request.result_id
|
|
|
|
|
+ username = current_user["sub"]
|
|
|
|
|
+ user_answers = user_answers_db.get(username, [])
|
|
|
|
|
+
|
|
|
|
|
+ if form_id not in BUSINESS_FORMS:
|
|
|
|
|
+ raise HTTPException(status_code=404, detail="Business form not found")
|
|
|
|
|
+
|
|
|
|
|
+ if not user_answers:
|
|
|
|
|
+ raise HTTPException(status_code=400, detail="No answers found")
|
|
|
|
|
+
|
|
|
|
|
+ form_info = BUSINESS_FORMS[form_id]
|
|
|
|
|
+
|
|
|
|
|
+ # Собираем ответы пользователя для контекста
|
|
|
|
|
+ answer_summary = []
|
|
|
|
|
+ for answer in user_answers:
|
|
|
|
|
+ question = next((q for q in questions if q["id"] == answer.question_id), None)
|
|
|
|
|
+ if question:
|
|
|
|
|
+ answer_text = [
|
|
|
|
|
+ question["ans_1"],
|
|
|
|
|
+ question["ans_2"],
|
|
|
|
|
+ question["ans_3"],
|
|
|
|
|
+ question["ans_4"]
|
|
|
|
|
+ ][answer.answer_index]
|
|
|
|
|
+ answer_summary.append(f"{question['text']}: {answer_text}")
|
|
|
|
|
+
|
|
|
|
|
+ # Формируем детальное описание
|
|
|
|
|
+ detailed_description = f"""
|
|
|
|
|
+ {form_info['name']}
|
|
|
|
|
+
|
|
|
|
|
+ {form_info['description']}
|
|
|
|
|
+
|
|
|
|
|
+ 📊 Основные характеристики:
|
|
|
|
|
+ • Налогообложение: {form_info['taxes']}
|
|
|
|
|
+ • Ответственность: {form_info['liability']}
|
|
|
|
|
+ • Стоимость регистрации: {form_info['registration_cost']}
|
|
|
|
|
+
|
|
|
|
|
+ ✅ Преимущества:
|
|
|
|
|
+ {chr(10).join(['• ' + adv for adv in form_info['advantages']])}
|
|
|
|
|
+
|
|
|
|
|
+ ⚠️ Недостатки:
|
|
|
|
|
+ {chr(10).join(['• ' + dis for dis in form_info['disadvantages']])}
|
|
|
|
|
+
|
|
|
|
|
+ 🎯 Лучше всего подходит для: {form_info['best_for']}
|
|
|
|
|
+
|
|
|
|
|
+ На основе ваших ответов, эта форма предпринимательства соответствует вашим требованиям.
|
|
|
|
|
+ """
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ "result_id": form_id,
|
|
|
|
|
+ "name": form_info["name"],
|
|
|
|
|
+ "description": detailed_description,
|
|
|
|
|
+ "advantages": form_info["advantages"],
|
|
|
|
|
+ "disadvantages": form_info["disadvantages"],
|
|
|
|
|
+ "taxes": form_info["taxes"],
|
|
|
|
|
+ "liability": form_info["liability"]
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+# 11. Эндпоинт для деталей через LLM
|
|
|
|
|
+@app.post("/details-llm")
|
|
|
|
|
+async def get_result_details_llm(request: ResultRequest, current_user: dict = Depends(get_current_user)):
|
|
|
|
|
+ username = current_user["sub"]
|
|
|
|
|
+ user_answers = user_answers_db.get(username, [])
|
|
|
|
|
+
|
|
|
|
|
+ if request.result_id not in BUSINESS_FORMS:
|
|
|
|
|
+ raise HTTPException(status_code=404, detail="Business form not found")
|
|
|
|
|
+
|
|
|
|
|
+ if not user_answers:
|
|
|
|
|
+ raise HTTPException(status_code=400, detail="No answers found")
|
|
|
|
|
+
|
|
|
|
|
+ form_info = BUSINESS_FORMS[request.result_id]
|
|
|
|
|
+
|
|
|
|
|
+ # Формируем системный промпт для LLM
|
|
|
|
|
+ system_prompt = """
|
|
|
|
|
+ Ты - опытный консультант по бизнесу и предпринимательству в РФ.
|
|
|
|
|
+ Твоя задача - дать развернутую, персонализированную консультацию о форме предпринимательства на основе ответов пользователя.
|
|
|
|
|
+ Будь конкретен, давай практические советы и учитывай особенности российского законодательства.
|
|
|
|
|
+ """
|
|
|
|
|
+
|
|
|
|
|
+ # Формируем пользовательский промпт с контекстом
|
|
|
|
|
+ user_prompt = f"""
|
|
|
|
|
+ Пользователь рассматривает форму предпринимательства: {form_info['name']}
|
|
|
|
|
+
|
|
|
|
|
+ Основная информация о форме:
|
|
|
|
|
+ - {form_info['description']}
|
|
|
|
|
+ - Налогообложение: {form_info['taxes']}
|
|
|
|
|
+ - Ответственность: {form_info['liability']}
|
|
|
|
|
+ - Стоимость регистрации: {form_info['registration_cost']}
|
|
|
|
|
+ - Преимущества: {', '.join(form_info['advantages'])}
|
|
|
|
|
+ - Недостатки: {', '.join(form_info['disadvantages'])}
|
|
|
|
|
+
|
|
|
|
|
+ Ответы пользователя в тесте:
|
|
|
|
|
+ """
|
|
|
|
|
+
|
|
|
|
|
+ # Добавляем ответы пользователя
|
|
|
|
|
+ for answer in user_answers:
|
|
|
|
|
+ question = next((q for q in questions if q["id"] == answer.question_id), None)
|
|
|
|
|
+ if question:
|
|
|
|
|
+ answer_text = [
|
|
|
|
|
+ question["ans_1"],
|
|
|
|
|
+ question["ans_2"],
|
|
|
|
|
+ question["ans_3"],
|
|
|
|
|
+ question["ans_4"]
|
|
|
|
|
+ ][answer.answer_index]
|
|
|
|
|
+ user_prompt += f"\n- {question['text']}: {answer_text}"
|
|
|
|
|
+
|
|
|
|
|
+ user_prompt += f"""
|
|
|
|
|
+
|
|
|
|
|
+ Проанализируй ответы пользователя и дай развернутую консультацию:
|
|
|
|
|
+ 1. Почему эта форма предпринимательства подходит/не подходит пользователю
|
|
|
|
|
+ 2. Какие конкретные шаги нужно предпринять для регистрации
|
|
|
|
|
+ 3. На какие подводные камни обратить внимание
|
|
|
|
|
+ 4. Какие налоговые льготы или программы поддержки можно использовать
|
|
|
|
|
+ 5. Практические рекомендации по ведению бизнеса в этой форме
|
|
|
|
|
+
|
|
|
|
|
+ Будь максимально конкретным и полезным!
|
|
|
|
|
+ """
|
|
|
|
|
+
|
|
|
|
|
+ # Отправляем запрос к LLM (пример для OpenAI)
|
|
|
|
|
+ try:
|
|
|
|
|
+ async with httpx.AsyncClient() as client:
|
|
|
|
|
+ response = await client.post(
|
|
|
|
|
+ LLM_API_URL,
|
|
|
|
|
+ headers={
|
|
|
|
|
+ "Authorization": f"Bearer {LLM_API_KEY}",
|
|
|
|
|
+ "Content-Type": "application/json"
|
|
|
|
|
+ },
|
|
|
|
|
+ json={
|
|
|
|
|
+ "model": "gpt-3.5-turbo",
|
|
|
|
|
+ "messages": [
|
|
|
|
|
+ {"role": "system", "content": system_prompt},
|
|
|
|
|
+ {"role": "user", "content": user_prompt}
|
|
|
|
|
+ ],
|
|
|
|
|
+ "max_tokens": 1500,
|
|
|
|
|
+ "temperature": 0.7
|
|
|
|
|
+ },
|
|
|
|
|
+ timeout=30.0
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ if response.status_code == 200:
|
|
|
|
|
+ llm_response = response.json()
|
|
|
|
|
+ llm_description = llm_response["choices"][0]["message"]["content"]
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ "result_id": request.result_id,
|
|
|
|
|
+ "name": form_info["name"],
|
|
|
|
|
+ "description": llm_description,
|
|
|
|
|
+ "llm_generated": True,
|
|
|
|
|
+ "model": "gpt-3.5-turbo"
|
|
|
|
|
+ }
|
|
|
|
|
+ else:
|
|
|
|
|
+ # Fallback если LLM не доступен
|
|
|
|
|
+ return await get_result_details(request, current_user)
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ # В случае ошибки возвращаем стандартное описание
|
|
|
|
|
+ print(f"LLM API error: {e}")
|
|
|
|
|
+ return await get_result_details(request, current_user)
|
|
|
|
|
+
|
|
|
|
|
+if __name__ == "__main__":
|
|
|
|
|
+ import uvicorn
|
|
|
|
|
+ uvicorn.run(app, host="0.0.0.0", port=8000)
|