Spaces:
Runtime error
Runtime error
Commit ·
8d9da4b
1
Parent(s): 604b114
Pivot: Decoupled Qwen AI Intelligence Provider. Only Qwen model on HF.
Browse files- Dockerfile +7 -10
- qwen_api.py +170 -0
- requirements.qwen.txt +11 -0
- scraper.py +45 -0
Dockerfile
CHANGED
|
@@ -25,19 +25,17 @@ RUN apt-get update && apt-get install -y \
|
|
| 25 |
USER user
|
| 26 |
|
| 27 |
# Install Python dependencies
|
| 28 |
-
COPY --chown=user requirements.txt .
|
| 29 |
RUN pip install --no-cache-dir --upgrade pip
|
| 30 |
|
| 31 |
# CRITICAL: Use official pre-compiled CPU wheels for the Qwen engine to bypass OOM errors
|
| 32 |
RUN pip install --no-cache-dir \
|
| 33 |
--extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cpu \
|
| 34 |
-
-r requirements.txt
|
| 35 |
|
| 36 |
-
#
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
# Copy the application code
|
| 40 |
-
COPY --chown=user . .
|
| 41 |
|
| 42 |
# Create the models directory with correct permissions
|
| 43 |
RUN mkdir -p ml_models && chmod 777 ml_models
|
|
@@ -45,6 +43,5 @@ RUN mkdir -p ml_models && chmod 777 ml_models
|
|
| 45 |
# Expose the port HF Spaces expects
|
| 46 |
EXPOSE 7860
|
| 47 |
|
| 48 |
-
# Run the
|
| 49 |
-
|
| 50 |
-
CMD ["gunicorn", "app:app", "-w", "1", "-k", "uvicorn.workers.UvicornWorker", "--bind", "0.0.0.0:7860", "--timeout", "600"]
|
|
|
|
| 25 |
USER user
|
| 26 |
|
| 27 |
# Install Python dependencies
|
| 28 |
+
COPY --chown=user requirements.qwen.txt .
|
| 29 |
RUN pip install --no-cache-dir --upgrade pip
|
| 30 |
|
| 31 |
# CRITICAL: Use official pre-compiled CPU wheels for the Qwen engine to bypass OOM errors
|
| 32 |
RUN pip install --no-cache-dir \
|
| 33 |
--extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cpu \
|
| 34 |
+
-r requirements.qwen.txt
|
| 35 |
|
| 36 |
+
# Copy only the essential AI application files
|
| 37 |
+
COPY --chown=user qwen_api.py .
|
| 38 |
+
COPY --chown=user scraper.py .
|
|
|
|
|
|
|
| 39 |
|
| 40 |
# Create the models directory with correct permissions
|
| 41 |
RUN mkdir -p ml_models && chmod 777 ml_models
|
|
|
|
| 43 |
# Expose the port HF Spaces expects
|
| 44 |
EXPOSE 7860
|
| 45 |
|
| 46 |
+
# Run the specialized Qwen API
|
| 47 |
+
CMD ["python", "qwen_api.py"]
|
|
|
qwen_api.py
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import uuid
|
| 3 |
+
import threading
|
| 4 |
+
from fastapi import FastAPI, HTTPException, Request
|
| 5 |
+
from pydantic import BaseModel
|
| 6 |
+
from typing import List, Optional
|
| 7 |
+
from huggingface_hub import hf_hub_download
|
| 8 |
+
from llama_cpp import Llama
|
| 9 |
+
from datetime import datetime
|
| 10 |
+
|
| 11 |
+
# Configuration
|
| 12 |
+
MODEL_DIR = "ml_models"
|
| 13 |
+
MODEL_NAME = "qwen-3.5-4b.gguf"
|
| 14 |
+
QWEN_PATH = os.path.join(MODEL_DIR, MODEL_NAME)
|
| 15 |
+
|
| 16 |
+
app = FastAPI(title="Qwen Intelligence Provider", version="1.0.0")
|
| 17 |
+
|
| 18 |
+
# Global LLM instance
|
| 19 |
+
_llm = None
|
| 20 |
+
_lock = threading.Lock()
|
| 21 |
+
|
| 22 |
+
def get_qwen():
|
| 23 |
+
global _llm
|
| 24 |
+
with _lock:
|
| 25 |
+
if _llm is None:
|
| 26 |
+
if not os.path.exists(QWEN_PATH):
|
| 27 |
+
print("📥 Downloading Qwen 3.5 (4B) from Hugging Face...")
|
| 28 |
+
os.makedirs(MODEL_DIR, exist_ok=True)
|
| 29 |
+
hf_hub_download(
|
| 30 |
+
repo_id="bartowski/Qwen_Qwen3.5-4B-GGUF",
|
| 31 |
+
filename="Qwen3.5-4B-Instruct-Q4_K_M.gguf",
|
| 32 |
+
local_dir=MODEL_DIR,
|
| 33 |
+
local_dir_use_symlinks=False
|
| 34 |
+
)
|
| 35 |
+
downloaded = os.path.join(MODEL_DIR, "Qwen3.5-4B-Instruct-Q4_K_M.gguf")
|
| 36 |
+
if os.path.exists(downloaded):
|
| 37 |
+
os.rename(downloaded, QWEN_PATH)
|
| 38 |
+
|
| 39 |
+
print(f"🚀 Initializing Qwen engine at {QWEN_PATH}")
|
| 40 |
+
_llm = Llama(
|
| 41 |
+
model_path=QWEN_PATH,
|
| 42 |
+
n_ctx=2048,
|
| 43 |
+
n_threads=4, # Optimized for HF Space CPUs
|
| 44 |
+
verbose=False
|
| 45 |
+
)
|
| 46 |
+
return _llm
|
| 47 |
+
|
| 48 |
+
# --- Data Models ---
|
| 49 |
+
|
| 50 |
+
class KeywordRequest(BaseModel):
|
| 51 |
+
idea_text: str
|
| 52 |
+
|
| 53 |
+
class ValidationRequest(BaseModel):
|
| 54 |
+
title1: str
|
| 55 |
+
title2: str
|
| 56 |
+
|
| 57 |
+
class OverviewRequest(BaseModel):
|
| 58 |
+
neg_reviews: List[str]
|
| 59 |
+
pos_reviews: List[str]
|
| 60 |
+
|
| 61 |
+
class SynthesisRequest(BaseModel):
|
| 62 |
+
overview_text: str
|
| 63 |
+
|
| 64 |
+
class BattleRequest(BaseModel):
|
| 65 |
+
app_a_name: str
|
| 66 |
+
app_a_overview: str
|
| 67 |
+
app_b_name: str
|
| 68 |
+
app_b_overview: str
|
| 69 |
+
|
| 70 |
+
# --- Endpoints ---
|
| 71 |
+
|
| 72 |
+
@app.get("/")
|
| 73 |
+
async def root():
|
| 74 |
+
return {"status": "online", "model": "Qwen 3.5 4B", "service": "Intelligence Provider"}
|
| 75 |
+
|
| 76 |
+
@app.post("/keywords")
|
| 77 |
+
async def extract_keywords(req: KeywordRequest):
|
| 78 |
+
llm = get_qwen()
|
| 79 |
+
prompt = f"""<|start_header_id|>system<|end_header_id|>
|
| 80 |
+
You are a expert market research assistant.
|
| 81 |
+
Extract 4-5 high-quality, comma-separated keywords or app types that would help find the most relevant competitors for the provided app idea description.
|
| 82 |
+
Output ONLY the comma-separated keywords. No conversational filler.
|
| 83 |
+
<|eot_id|><|start_header_id|>user<|end_header_id|>
|
| 84 |
+
App Idea: {req.idea_text}
|
| 85 |
+
<|eot_id|><|start_header_id|>assistant<|end_header_id|>"""
|
| 86 |
+
res = llm(prompt, max_tokens=100, stop=["<|eot_id|>"], echo=False)
|
| 87 |
+
return {"keywords": res['choices'][0]['text'].strip()}
|
| 88 |
+
|
| 89 |
+
@app.post("/validate")
|
| 90 |
+
async def validate_match(req: ValidationRequest):
|
| 91 |
+
llm = get_qwen()
|
| 92 |
+
prompt = f"""<|start_header_id|>system<|end_header_id|>
|
| 93 |
+
You are an expert AI product assistant. Reply ONLY with "YES" if these two titles represent the same application, or "NO" if they are clearly different products.
|
| 94 |
+
Example: "WhatsApp" and "WhatsApp Messenger" is YES. "WhatsApp" and "Instagram" is NO.
|
| 95 |
+
<|eot_id|><|start_header_id|>user<|end_header_id|>
|
| 96 |
+
App Name 1: {req.title1}
|
| 97 |
+
App Name 2: {req.title2}
|
| 98 |
+
<|eot_id|><|start_header_id|>assistant<|end_header_id|>"""
|
| 99 |
+
res = llm(prompt, max_tokens=10, stop=["<|eot_id|>"], echo=False)
|
| 100 |
+
return {"match": res['choices'][0]['text'].strip().upper()}
|
| 101 |
+
|
| 102 |
+
@app.post("/overview")
|
| 103 |
+
async def generate_overview(req: OverviewRequest):
|
| 104 |
+
llm = get_qwen()
|
| 105 |
+
|
| 106 |
+
def get_mini_summary(items, sentiment):
|
| 107 |
+
if not items: return ""
|
| 108 |
+
# Keep only a sample to avoid context overflow in mini-summaries
|
| 109 |
+
sample = items[:15]
|
| 110 |
+
prompt = f"""<|start_header_id|>system<|end_header_id|>
|
| 111 |
+
You are a technical analyst. Summarize the provided {sentiment} reviews into a single dense bullet point of insights.
|
| 112 |
+
Do NOT include any filler.
|
| 113 |
+
<|eot_id|><|start_header_id|>user<|end_header_id|>
|
| 114 |
+
Reviews: {chr(10).join(['- ' + r[:200] for r in sample])}
|
| 115 |
+
<|eot_id|><|start_header_id|>assistant<|end_header_id|>"""
|
| 116 |
+
res = llm(prompt, max_tokens=150, stop=["<|eot_id|>"], echo=False)
|
| 117 |
+
return res['choices'][0]['text'].strip()
|
| 118 |
+
|
| 119 |
+
neg_mini = get_mini_summary(req.neg_reviews, "negative")
|
| 120 |
+
pos_mini = get_mini_summary(req.pos_reviews, "positive")
|
| 121 |
+
|
| 122 |
+
master_prompt = f"""<|start_header_id|>system<|end_header_id|>
|
| 123 |
+
You are a senior product manager. Based on the hierarchical insights provided, write a deep 3-paragraph professional analysis.
|
| 124 |
+
Structure:
|
| 125 |
+
- Paragraph 1: UX/UI and technical critical complaints.
|
| 126 |
+
- Paragraph 2: Core value propositions and high-impact features.
|
| 127 |
+
- Paragraph 3: Future roadmap and growth strategy.
|
| 128 |
+
CRITICAL: Start response IMMEDIATELY. No intro.
|
| 129 |
+
<|eot_id|><|start_header_id|>user<|end_header_id|>
|
| 130 |
+
NEGATIVE THEMES: {neg_mini}
|
| 131 |
+
POSITIVE THEMES: {pos_mini}
|
| 132 |
+
<|eot_id|><|start_header_id|>assistant<|end_header_id|>"""
|
| 133 |
+
|
| 134 |
+
res = llm(master_prompt, max_tokens=600, stop=["<|eot_id|>"], echo=False)
|
| 135 |
+
return {"overview": res['choices'][0]['text'].strip()}
|
| 136 |
+
|
| 137 |
+
@app.post("/synthesis")
|
| 138 |
+
async def generate_synthesis(req: SynthesisRequest):
|
| 139 |
+
llm = get_qwen()
|
| 140 |
+
prompt = f"""<|start_header_id|>system<|end_header_id|>
|
| 141 |
+
You are a elite strategic advisor. Condense the provided multi-paragraph analysis into a single, punchy, high-impact paragraph (max 40 words).
|
| 142 |
+
Focus on the "bottom line". Output ONLY the paragraph.
|
| 143 |
+
<|eot_id|><|start_header_id|>user<|end_header_id|>
|
| 144 |
+
Full Analysis: {req.overview_text}
|
| 145 |
+
<|eot_id|><|start_header_id|>assistant<|end_header_id|>"""
|
| 146 |
+
res = llm(prompt, max_tokens=150, stop=["<|eot_id|>"], echo=False)
|
| 147 |
+
return {"synthesis": res['choices'][0]['text'].strip()}
|
| 148 |
+
|
| 149 |
+
@app.post("/battle")
|
| 150 |
+
async def generate_battle(req: BattleRequest):
|
| 151 |
+
llm = get_qwen()
|
| 152 |
+
prompt = f"""<|start_header_id|>system<|end_header_id|>
|
| 153 |
+
You are an elite competitive intelligence officer. Perform a "Neural Clash" analysis between two competing applications.
|
| 154 |
+
Contrast their strengths, expose their fatal flaws, and declare a strategic winner.
|
| 155 |
+
Structure:
|
| 156 |
+
1. WINNING EDGE: Who has the superior UX?
|
| 157 |
+
2. VULNERABILITY GAP: Biggest retention killer?
|
| 158 |
+
3. STRATEGIC VERDICT: Superior growth trajectory?
|
| 159 |
+
<|eot_id|><|start_header_id|>user<|end_header_id|>
|
| 160 |
+
APP A ({req.app_a_name}): {req.app_a_overview}
|
| 161 |
+
APP B ({req.app_b_name}): {req.app_b_overview}
|
| 162 |
+
<|eot_id|><|start_header_id|>assistant<|end_header_id|>"""
|
| 163 |
+
res = llm(prompt, max_tokens=600, stop=["<|eot_id|>"], echo=False)
|
| 164 |
+
return {"battle": res['choices'][0]['text'].strip()}
|
| 165 |
+
|
| 166 |
+
if __name__ == "__main__":
|
| 167 |
+
import uvicorn
|
| 168 |
+
# Pre-warm model cache
|
| 169 |
+
get_qwen()
|
| 170 |
+
uvicorn.run(app, host="0.0.0.0", port=7860)
|
requirements.qwen.txt
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Optimized AI-Only Requirements for Qwen Intelligence Provider
|
| 2 |
+
--prefer-binary
|
| 3 |
+
--only-binary llama-cpp-python
|
| 4 |
+
|
| 5 |
+
fastapi==0.128.8
|
| 6 |
+
uvicorn[standard]==0.33.0
|
| 7 |
+
llama_cpp_python==0.3.19
|
| 8 |
+
huggingface_hub==0.36.2
|
| 9 |
+
python-dotenv==1.0.0
|
| 10 |
+
pydantic
|
| 11 |
+
requests
|
scraper.py
CHANGED
|
@@ -130,8 +130,22 @@ def search_app_store(query):
|
|
| 130 |
|
| 131 |
# Neural Config
|
| 132 |
QWEN_PATH = "ml_models/qwen-3.5-4b.gguf"
|
|
|
|
|
|
|
| 133 |
_Qwen = None
|
| 134 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
def get_qwen():
|
| 136 |
global _Qwen
|
| 137 |
if _Qwen: return _Qwen
|
|
@@ -197,6 +211,11 @@ Reviews to analyze:
|
|
| 197 |
pos_mini = get_mini_summary(pos_clusters, "positive")
|
| 198 |
|
| 199 |
# Phase 2: Master Synthesis
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
master_prompt = f"""<|start_header_id|>system<|end_header_id|>
|
| 201 |
You are a senior product manager. Based on the hierarchical insights provided, write a deep 3-paragraph professional analysis.
|
| 202 |
Structure:
|
|
@@ -235,6 +254,12 @@ Directly output the paragraph. No filler.
|
|
| 235 |
Full Analysis:
|
| 236 |
{overview_text}
|
| 237 |
<|eot_id|><|start_header_id|>assistant<|end_header_id|>"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 238 |
try:
|
| 239 |
res = llm(prompt, max_tokens=150, stop=["<|eot_id|>"], echo=False)
|
| 240 |
return res['choices'][0]['text'].strip(' "\n\r')
|
|
@@ -274,6 +299,14 @@ APPLICATION B: {name_b}
|
|
| 274 |
INTEL ON B: {analysis_b}
|
| 275 |
<|eot_id|><|start_header_id|>assistant<|end_header_id|>"""
|
| 276 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 277 |
try:
|
| 278 |
res = llm(prompt, max_tokens=600, stop=["<|eot_id|>"], echo=False)
|
| 279 |
return res['choices'][0]['text'].strip(' "\n\r')
|
|
@@ -295,6 +328,12 @@ Output ONLY the comma-separated keywords. No conversational filler.
|
|
| 295 |
<|eot_id|><|start_header_id|>user<|end_header_id|>
|
| 296 |
App Idea: {idea_text}
|
| 297 |
<|eot_id|><|start_header_id|>assistant<|end_header_id|>"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 298 |
try:
|
| 299 |
res = llm(prompt, max_tokens=100, stop=["<|eot_id|>"], echo=False)
|
| 300 |
return res['choices'][0]['text'].strip()
|
|
@@ -312,6 +351,12 @@ Example: "WhatsApp" and "WhatsApp Messenger" is YES. "WhatsApp" and "Instagram"
|
|
| 312 |
App Name 1: {title1}
|
| 313 |
App Name 2: {title2}
|
| 314 |
<|eot_id|><|start_header_id|>assistant<|end_header_id|>"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 315 |
try:
|
| 316 |
ans_res = llm(prompt, max_tokens=10, stop=["<|eot_id|>"], echo=False)
|
| 317 |
ans = ans_res['choices'][0]['text'].strip().upper()
|
|
|
|
| 130 |
|
| 131 |
# Neural Config
|
| 132 |
QWEN_PATH = "ml_models/qwen-3.5-4b.gguf"
|
| 133 |
+
# Hugging Face Intelligence Provider URL (if decoupled)
|
| 134 |
+
QWEN_REMOTE_URL = os.getenv("QWEN_REMOTE_URL")
|
| 135 |
_Qwen = None
|
| 136 |
|
| 137 |
+
def call_remote_qwen(endpoint: str, payload: dict):
|
| 138 |
+
"""Internal helper to communicate with the decoupled Qwen Intelligence Provider."""
|
| 139 |
+
if not QWEN_REMOTE_URL: return None
|
| 140 |
+
try:
|
| 141 |
+
url = f"{QWEN_REMOTE_URL.rstrip('/')}/{endpoint.lstrip('/')}"
|
| 142 |
+
res = requests.post(url, json=payload, timeout=60)
|
| 143 |
+
if res.status_code == 200:
|
| 144 |
+
return res.json()
|
| 145 |
+
except Exception as e:
|
| 146 |
+
print(f"[Remote AI Error] Failed to reach {endpoint}: {e}")
|
| 147 |
+
return None
|
| 148 |
+
|
| 149 |
def get_qwen():
|
| 150 |
global _Qwen
|
| 151 |
if _Qwen: return _Qwen
|
|
|
|
| 211 |
pos_mini = get_mini_summary(pos_clusters, "positive")
|
| 212 |
|
| 213 |
# Phase 2: Master Synthesis
|
| 214 |
+
if QWEN_REMOTE_URL:
|
| 215 |
+
remote_res = call_remote_qwen("overview", {"neg_reviews": neg_clusters, "pos_reviews": pos_clusters})
|
| 216 |
+
if remote_res and "overview" in remote_res:
|
| 217 |
+
return remote_res["overview"]
|
| 218 |
+
|
| 219 |
master_prompt = f"""<|start_header_id|>system<|end_header_id|>
|
| 220 |
You are a senior product manager. Based on the hierarchical insights provided, write a deep 3-paragraph professional analysis.
|
| 221 |
Structure:
|
|
|
|
| 254 |
Full Analysis:
|
| 255 |
{overview_text}
|
| 256 |
<|eot_id|><|start_header_id|>assistant<|end_header_id|>"""
|
| 257 |
+
|
| 258 |
+
if QWEN_REMOTE_URL:
|
| 259 |
+
remote_res = call_remote_qwen("synthesis", {"overview_text": overview_text})
|
| 260 |
+
if remote_res and "synthesis" in remote_res:
|
| 261 |
+
return remote_res["synthesis"]
|
| 262 |
+
|
| 263 |
try:
|
| 264 |
res = llm(prompt, max_tokens=150, stop=["<|eot_id|>"], echo=False)
|
| 265 |
return res['choices'][0]['text'].strip(' "\n\r')
|
|
|
|
| 299 |
INTEL ON B: {analysis_b}
|
| 300 |
<|eot_id|><|start_header_id|>assistant<|end_header_id|>"""
|
| 301 |
|
| 302 |
+
if QWEN_REMOTE_URL:
|
| 303 |
+
remote_res = call_remote_qwen("battle", {
|
| 304 |
+
"app_a_name": name_a, "app_a_overview": analysis_a,
|
| 305 |
+
"app_b_name": name_b, "app_b_overview": analysis_b
|
| 306 |
+
})
|
| 307 |
+
if remote_res and "battle" in remote_res:
|
| 308 |
+
return remote_res["battle"]
|
| 309 |
+
|
| 310 |
try:
|
| 311 |
res = llm(prompt, max_tokens=600, stop=["<|eot_id|>"], echo=False)
|
| 312 |
return res['choices'][0]['text'].strip(' "\n\r')
|
|
|
|
| 328 |
<|eot_id|><|start_header_id|>user<|end_header_id|>
|
| 329 |
App Idea: {idea_text}
|
| 330 |
<|eot_id|><|start_header_id|>assistant<|end_header_id|>"""
|
| 331 |
+
|
| 332 |
+
if QWEN_REMOTE_URL:
|
| 333 |
+
remote_res = call_remote_qwen("keywords", {"idea_text": idea_text})
|
| 334 |
+
if remote_res and "keywords" in remote_res:
|
| 335 |
+
return remote_res["keywords"]
|
| 336 |
+
|
| 337 |
try:
|
| 338 |
res = llm(prompt, max_tokens=100, stop=["<|eot_id|>"], echo=False)
|
| 339 |
return res['choices'][0]['text'].strip()
|
|
|
|
| 351 |
App Name 1: {title1}
|
| 352 |
App Name 2: {title2}
|
| 353 |
<|eot_id|><|start_header_id|>assistant<|end_header_id|>"""
|
| 354 |
+
|
| 355 |
+
if QWEN_REMOTE_URL:
|
| 356 |
+
remote_res = call_remote_qwen("validate", {"title1": title1, "title2": title2})
|
| 357 |
+
if remote_res and "match" in remote_res:
|
| 358 |
+
return remote_res["match"]
|
| 359 |
+
|
| 360 |
try:
|
| 361 |
ans_res = llm(prompt, max_tokens=10, stop=["<|eot_id|>"], echo=False)
|
| 362 |
ans = ans_res['choices'][0]['text'].strip().upper()
|