ollama-server3 / app.py
Mmfallah's picture
Update app.py
9442450 verified
Raw
History Blame Contribute Delete
14.6 kB
import os
import logging
import time
import threading
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import requests
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def heartbeat():
while True:
try:
requests.get("http://127.0.0.1:11434/api/tags", timeout=5)
except:
pass
time.sleep(10)
threading.Thread(target=heartbeat, daemon=True).start()
app = FastAPI()
OLLAMA_URL = os.getenv("OLLAMA_HOST", "http://localhost:11434")
queue_lock = threading.Lock()
active = 0
MAX = 2
class ChatRequest(BaseModel):
model: str = "hf.co/mradermacher/gemma-3-4b-persian-v0-abliterated-GGUF:Q4_K_M"
messages: list
temperature: float = 0.7
stream: bool = False
options: dict = {}
# ═══ کلمات کلیدی گسترده برای تشخیص کد ═══
CODE_KEYWORDS = [
# ─── برنامه‌نویسی عمومی ───
"کد", "code", "کدنویسی", "برنامه‌نویسی", "برنامه نویسی", "programming",
"سورس", "source", "اسکریپت", "script", "اسکریپتینگ",
"فانکشن", "function", "تابع", "متد", "method", "کلاس", "class",
"آبجکت", "object", "اینترفیس", "interface", "ماژول", "module",
"پکیج", "package", "لایبرری", "library", "فریم‌ورک", "framework",
# ─── زبان‌های برنامه‌نویسی ───
"python", "پایتون", "جاوااسکریپت", "javascript", "js", "تایپ‌اسکریپت", "typescript", "ts",
"جاوا", "java", "سی‌شارپ", "csharp", "c#", "سی‌پلاس‌پلاس", "cpp", "c++", "سی", "c",
"گو", "go", "روبی", "ruby", "پی‌اچ‌پی", "php", "لاراول", "laravel",
"سوئیفت", "swift", "کاتلین", "kotlin", "rust", "راست",
"اسکالا", "scala", "پرل", "perl", "lua", "لوآ",
"آر", "r", "متلب", "matlab", "فرترن", "fortran",
"کوبول", "cobol", "پاسکال", "pascal", "دلفی", "delphi",
"اشمی", "bash", "شل", "shell", "پاورشل", "powershell",
"اچ‌تی‌ام‌ال", "html", "سی‌اس‌اس", "css", "اس‌کیوال", "sql",
"اچ‌تی‌ام‌ال", "xml", "جیسون", "json", "یمل", "yaml",
"مارک‌داون", "markdown", "لتکس", "latex", "رجکس", "regex", "ریجکس",
# ─── دیتابیس ───
"دیتابیس", "database", "دیتا بیس", "بانک اطلاعاتی",
"mysql", "ماریا", "mariadb", "پستگرس", "postgresql", "پستگرس‌کیوال",
"مونگو", "mongodb", "ردیس", "redis", "کاساندرا", "cassandra",
"الاستیک", "elasticsearch", "داینامو", "dynamodb", "فایربیس", "firebase",
"اسکیوالایت", "sqlite", "اوراکل", "oracle", "مایکروسافت اس‌کیوال", "mssql",
"جدول", "table", "کوئری", "query", "کوری", "سلکت", "select", "اینزرت", "insert",
"آپدیت", "update", "دیلیت", "delete", "جوین", "join", "ایندکس", "index",
# ─── شبکه و امنیت ───
"اسکن", "scan", "پورت", "port", "اسکن پورت", "port scan", "پورت اسکن",
"نفوذ", "penetration", "تست نفوذ", "pentest", "هایکینگ", "hacking",
"کالی", "kali", "متاسپلویت", "metasploit", "بورپ", "burp",
"انمپ", "nmap", "مپ", "map", "زمپ", "zmap", "ماسکِن", "masscan",
"نیسوس", "nessus", "اوپن‌وس", "openvas", "نکستوس", "nexpose",
"وایرشارک", "wireshark", "tcpdump", "تنکپ", "tshark",
"پروکسی", "proxy", "vpn", "وی‌پی‌ان", "تور", "tor",
"فایروال", "firewall", "iptables", "یوف‌ dubois", "ufw",
"دی‌ان‌اس", "dns", "dhcp", "دی‌اچ‌سی‌پی", "آی‌پی", "ip", "آدرس آی‌پی",
"مک", "mac", "آدرس مک", "روتر", "router", "سوئیچ", "switch",
"لود بالانسر", "load balancer", "سی‌دی‌ان", "cdn",
"اس‌اس‌اچ", "ssh", "تلنت", "telnet", "اف‌تی‌پی", "ftp", "اس‌اف‌تی‌پی", "sftp",
"اس‌ام‌تی‌پی", "smtp", "پاپ۳", "pop3", "آی‌ام‌ای‌پی", "imap",
"http", "https", "اس‌اس‌ال", "ssl", "تی‌ال‌اس", "tls", "سرتیفیکیت",
"دومین", "domain", "ساب‌دامین", "subdomain", "هاست", "host",
"پینگ", "ping", "تریس‌روت", "traceroute", "nslookup", "دیگ", "dig",
"netcat", "ان‌سی", "nc", "socat", "سوکت", "socket",
# ─── وب و API ───
"api", "ای‌پی‌آی", "رست", "rest", "گراف‌کیوال", "graphql", "grpc",
"soap", "وب‌سرویس", "webservice", "میکروسرویس", "microservice",
"کرال", "crawl", "اسکرپ", "scrape", "وب‌اسکرپینگ", "web scraping",
"سلنیوم", "selenium", "پلی‌رایت", "playwright", "پاپتیر", "puppeteer",
"بیوتیفول‌سoup", "beautifulsoup", "اسکرپی", "scrapy",
"فست‌api", "fastapi", "فلسک", "flask", "جانگو", "django", "تورنادو", "tornado",
"اکسپرس", "express", "نست", "nest", "نکست", "next", "نکست‌جی‌اس", "nextjs",
"ریکت", "react", "ویو", "vue", "انگولار", "angular", "اس‌وی‌الت", "svelte",
"بوت‌استرپ", "bootstrap", "تیلویند", "tailwind", "متریال", "material",
# ─── سیستم‌عامل و داکر ───
"لینوکس", "linux", "اوبونتو", "ubuntu", "دبیان", "debian", "سنت‌او‌اس", "centos",
"ردهت", "redhat", "فدورا", "fedora", "آرچ", "arch", "مانجارو", "manjaro",
"ویندوز", "windows", "مک", "mac", "مک‌او‌اس", "macos",
"داکر", "docker", "کانتینر", "container", "کوبرنتیز", "kubernetes", "k8s",
"هلم", "helm", "jenkins", "جنکینز", "ci/cd", "سی‌آی/سی‌دی",
"ansible", "انسیبل", "ترافورم", "terraform", "پاکر", "packer",
"وگرانت", "vagrant", "پروتیوس", "prometheus", "گرافانا", "grafana",
# ─── گیت و ورژن کنترل ───
"گیت", "git", "گیت‌هاب", "github", "گیت‌لب", "gitlab", "بیت‌باکت", "bitbucket",
"کامیت", "commit", "پوش", "push", "پول", "pull", "مرج", "merge", "برنچ", "branch",
"تگ", "tag", "ریبیس", "rebase", "چری‌پیک", "cherry-pick", "استش", "stash",
"کلون", "clone", "فورک", "fork", "پول‌ریکوئست", "pull request", "pr",
# ─── کلود و سرور ───
"aws", "ای‌دی‌بلیو‌اس", "آمازون", "azure", "آژور", "gcp", "جی‌سی‌پی", "گوگل کلود",
"ec2", "اس۳", "s3", "rds", "لامبدا", "lambda", "کلودفرانت", "cloudfront",
"ورسل", "vercel", "نتلیفای", "netlify", "هروکو", "heroku",
"دیجیتال اوشن", "digitalocean", "لینود", "linode", "ولچر", "vultr",
"انجین‌اکس", "nginx", "آپاچی", "apache", "تامکت", "tomcat",
# ─── هوش مصنوعی و دیتا ───
"هوش مصنوعی", "ai", "ماشین لرنینگ", "machine learning", "ml",
"دیپ لرنینگ", "deep learning", "شبکه عصبی", "neural network",
"تنسورفلو", "tensorflow", "کراس", "keras", "پایتورچ", "pytorch",
"سایکیت‌لرن", "scikit-learn", "پانداس", "pandas", "نمپای", "numpy",
"matplotlib", "سیبورن", "seaborn", "پلاتلی", "plotly",
"jupyter", "ژوپیتر", "کولب", "colab", "آناکوندا", "anaconda",
"opencv", "اچ‌پی‌ال", "hugging face", "ترنسفورمرز", "transformers",
"llm", "مدل زبانی", "chatgpt", "جی‌پی‌تی", "gpt", "bert", "الما",
"پرامپت", "prompt", "پرامپت انجینرینگ", "prompt engineering",
"آر‌اَگ", "rag", "embedding", "امبدینگ", "وکتور", "vector",
# ─── الگوریتم و ساختمان داده ───
"الگوریتم", "algorithm", "ساختمان داده", "data structure",
"لیست", "list", "آرایه", "array", "دیکشنری", "dictionary", "dict", "هش‌مپ", "hashmap",
"تاپل", "tuple", "ست", "set", "استک", "stack", "کیو", "queue", "دک", "deque",
"لیست پیوندی", "linked list", "درخت", "tree", "باینری", "binary",
"گراف", "graph", "هیپ", "heap", "تری", "trie", "بلوم فیلتر", "bloom filter",
"سورت", "sort", "مرتب‌سازی", "سرچ", "search", "جستجو",
"بینری سرچ", "binary search", "دی‌اف‌اس", "dfs", "بی‌اف‌اس", "bfs",
"دایناسیک", "dynamic programming", "dp", "گرِدی", "greedy",
"بک‌ترکینگ", "backtracking", "دیواید اند کانکر", "divide and conquer",
"ریکرژن", "recursion", "تکرار", "iteration", "مموایزیشن", "memoization",
# ─── تست و دیباگ ───
"تست", "test", "یونیت تست", "unit test", "اینتگرشن تست", "integration test",
"e2e", "اند تو اند", "تست خودکار", "automation test",
"دیباگ", "debug", "دیباگر", "debugger", "لاگ", "log", "لاگینگ", "logging",
"پروفایلینگ", "profiling", "بهینه‌سازی", "optimization", "پرفورمنس", "performance",
"ریفکتور", "refactor", "کلین کد", "clean code", "دیزاین پترن", "design pattern",
"سالید", "solid", "dry", "kiss", "yagni",
# ─── موبایل ───
"اندروید", "android", "آی‌او‌اس", "ios", "فلاتر", "flutter", "دارت", "dart",
"ریکت نیتیو", "react native", "کوردوا", "cordova", "آیونیک", "ionic",
"سوئیفت", "swift", "کاتلین", "kotlin", "جتپک کامپوز", "jetpack compose",
# ─── بلاکچین ───
"بلاکچین", "blockchain", "بیت‌کوین", "bitcoin", "اتریوم", "ethereum",
"قرارداد هوشمند", "smart contract", "سولیدیتی", "solidity",
"وب۳", "web3", "دی‌فای", "defi", "ان‌اف‌تی", "nft",
"ماینینگ", "mining", "استیکینگ", "staking", "ولت", "wallet",
# ─── سخت‌افزار و IoT ───
"آردوینو", "arduino", "رسپبری", "raspberry", "اسپ۳۲", "esp32",
"اینترنت اشیاء", "iot", "سنسور", "sensor", "میکروکنترلر", "microcontroller",
"gpio", "جی‌پی‌آی‌او", "pwm", "آی۲سی", "i2c", "اس‌پی‌آی", "spi", "یوارت", "uart",
# ─── فرمت‌ها و ابزارها ───
"csv", "اکسل", "excel", "pdf", "ورد", "word", "json", "xml", "yaml",
"docker-compose", "dockerfile", "makefile", "cmake", "gradle", "maven",
"npm", "ین‌پی‌ام", "pip", "کندا", "conda", "venv", "virtualenv",
"requirements", "package.json", "cargo", "composer",
# ─── کلمات فارسی مرتبط ───
"بنویس", "بساز", "بده", "نمایش بده", "چاپ کن", "اجرا کن", "run",
"نمونه", "مثال", "example", "sample", "تمپلیت", "template", "boilerplate",
"چطوری", "چگونه", "how to", "آموزش", "tutorial", "راهنما", "guide",
"مشکل", "خطا", "error", "باگ", "bug", "issue", "ایشو",
"راه‌حل", "solution", "fix", "فیکس", "پچ", "patch",
]
def is_code_request(messages: list) -> bool:
"""تشخیص درخواست کد از پیام آخر کاربر"""
if not messages:
return False
text = messages[-1].get("content", "").lower()
return any(k in text for k in CODE_KEYWORDS)
@app.get("/")
def root():
return {"ok": True, "queue": {"active": active, "max": MAX}}
@app.get("/api/tags")
def tags():
try:
return requests.get(f"{OLLAMA_URL}/api/tags", timeout=10).json()
except Exception as e:
return {"error": str(e)}
@app.get("/health")
def health():
try:
r = requests.get(f"{OLLAMA_URL}/api/tags", timeout=5)
return {"ok": r.status_code == 200}
except:
return {"ok": False}
def wait_turn(timeout=60):
global active
start = time.time()
while True:
with queue_lock:
if active < MAX:
active += 1
return
if time.time() - start > timeout:
raise HTTPException(status_code=503, detail="سرور شلوغه. دوباره تلاش کنید")
time.sleep(0.5)
def release():
global active
with queue_lock:
active = max(0, active - 1)
@app.post("/api/chat")
def chat(req: ChatRequest):
wait_turn()
start = time.time()
try:
body = req.dict()
body["keep_alive"] = -1
if "options" not in body or not body["options"]:
body["options"] = {}
# ═══ تشخیص خودکار حالت ═══
code_mode = is_code_request(body.get("messages", []))
# حالت چت — کوتاه و سریع
body["options"]["num_predict"] = 400
body["options"]["num_ctx"] = 2048 # context کمتر
body["options"]["num_thread"] = 2
timeout = 75 # ۱ دقیقه
logger.info("Mode: CHAT (fast response)")
logger.info(f"Chat: model={body['model']}, predict={body['options']['num_predict']}, ctx={body['options']['num_ctx']}")
r = requests.post(
f"{OLLAMA_URL}/api/chat",
json=body,
timeout=timeout
)
elapsed = time.time() - start
logger.info(f"Done in {elapsed:.1f}s (mode={'CODE' if code_mode else 'CHAT'})")
return r.json()
except requests.exceptions.Timeout:
logger.error("Timeout!")
raise HTTPException(status_code=504, detail="timeout")
except Exception as e:
logger.error(f"Error: {e}")
raise HTTPException(status_code=500, detail=str(e))
finally:
release()
@app.on_event("startup")
async def startup():
logger.info("Proxy started with auto mode detection")
@app.on_event("shutdown")
async def shutdown():
logger.info("Proxy stopped")