289 lines
12 KiB
Python
289 lines
12 KiB
Python
"""
|
||
SeaFare Montana — Centralized Configuration
|
||
All hardcoded constants in one place.
|
||
"""
|
||
|
||
# =============================================================================
|
||
# APPLICATION
|
||
# =============================================================================
|
||
|
||
APP_VERSION = '3.44.0'
|
||
DEFAULT_PORT = 5050
|
||
|
||
ALLOWED_ORIGINS = [
|
||
"https://seafare.efir.org",
|
||
"https://seafare-montana.duckdns.org", # legacy
|
||
]
|
||
# Dev origins added dynamically if FLASK_DEBUG is set (see seafare_api.py)
|
||
|
||
# =============================================================================
|
||
# AI PROVIDERS — priority: mistral → cerebras → groq → openrouter (no claude)
|
||
# =============================================================================
|
||
|
||
# Provider selection: 'mistral', 'cerebras', 'groq', 'openrouter', 'auto' (tries in order)
|
||
AI_PROVIDER = 'auto'
|
||
|
||
# Groq (free, fast — Llama 3.3 70B)
|
||
GROQ_MODEL = "llama-3.3-70b-versatile"
|
||
GROQ_MODEL_LIGHT = "llama-3.1-8b-instant" # for casual chat
|
||
GROQ_MAX_TOKENS = 2048
|
||
GROQ_TIMEOUT = 15.0
|
||
ENV_GROQ_API_KEY = "GROQ_API_KEY"
|
||
|
||
# Mistral (cheap fallback)
|
||
MISTRAL_MODEL = "mistral-large-latest"
|
||
MISTRAL_MODEL_LIGHT = "mistral-small-latest"
|
||
MISTRAL_MAX_TOKENS = 2048
|
||
MISTRAL_TIMEOUT = 15.0
|
||
ENV_MISTRAL_API_KEY = "MISTRAL_API_KEY"
|
||
# OpenRouter (free tier — DeepSeek, Gemini Flash, etc.)
|
||
OPENROUTER_MODEL = 'meta-llama/llama-3.3-70b-instruct:free'
|
||
OPENROUTER_MODEL_LIGHT = 'meta-llama/llama-3.3-70b-instruct:free'
|
||
OPENROUTER_MAX_TOKENS = 2048
|
||
OPENROUTER_TIMEOUT = 45.0
|
||
OPENROUTER_BASE_URL = 'https://openrouter.ai/api/v1'
|
||
ENV_OPENROUTER_API_KEY = 'OPENROUTER_API_KEY'
|
||
# Cerebras (free — 1M tokens/day, OpenAI-compatible)
|
||
CEREBRAS_MODEL = 'qwen-3-235b-a22b-instruct-2507'
|
||
CEREBRAS_MODEL_LIGHT = 'llama3.1-8b'
|
||
CEREBRAS_MAX_TOKENS = 2048
|
||
CEREBRAS_TIMEOUT = 45.0
|
||
CEREBRAS_BASE_URL = 'https://api.cerebras.ai/v1'
|
||
ENV_CEREBRAS_API_KEY = 'CEREBRAS_API_KEY'
|
||
|
||
|
||
# Claude (kept for agent tools only — NOT in auto chain)
|
||
CLAUDE_MODEL = "claude-sonnet-4-5-20250929"
|
||
CLAUDE_MAX_TOKENS = 2048
|
||
CLAUDE_TIMEOUT = 45.0
|
||
|
||
AGENT_MAX_TOOL_ITERATIONS = 4
|
||
|
||
# Per-session budget control (input tokens)
|
||
AGENT_BUDGET_SOFT_INPUT = 5000 # inject "be concise" hint after this
|
||
AGENT_BUDGET_HARD_INPUT = 12000 # force stop loop after this
|
||
|
||
# Claude pricing ($/1M tokens, Sonnet 4.5)
|
||
CLAUDE_PRICE_INPUT = 3.0
|
||
CLAUDE_PRICE_OUTPUT = 15.0
|
||
CLAUDE_PRICE_CACHE_WRITE = 3.75
|
||
CLAUDE_PRICE_CACHE_READ = 0.30
|
||
|
||
# Moltbook integration
|
||
MOLTBOOK_BASE_URL = "https://www.moltbook.com/api/v1"
|
||
|
||
# Contact unlock price (currently free)
|
||
CONTACT_UNLOCK_PRICE = 0.0
|
||
|
||
# =============================================================================
|
||
# AUTH
|
||
# =============================================================================
|
||
|
||
TOKEN_MAX_AGE = 30 * 24 * 3600 # 30 days in seconds
|
||
|
||
# =============================================================================
|
||
# RATE LIMITS
|
||
# =============================================================================
|
||
|
||
RATE_LIMIT_DEFAULT_MAX_CALLS = 30
|
||
RATE_LIMIT_DEFAULT_PERIOD = 60 # seconds
|
||
|
||
RATE_LIMIT_CHAT_AUTH_MAX = 30 # authenticated user chat
|
||
RATE_LIMIT_CHAT_AUTH_PERIOD = 60
|
||
|
||
RATE_LIMIT_CHAT_ANON_MAX = 5 # anonymous user chat
|
||
RATE_LIMIT_CHAT_ANON_PERIOD = 3600
|
||
|
||
RATE_LIMIT_CLEANUP_INTERVAL = 300 # bucket cleanup every 5 min
|
||
|
||
# =============================================================================
|
||
# CACHING
|
||
# =============================================================================
|
||
|
||
POSITION_CACHE_MINUTES = 5
|
||
VESSEL_CACHE_HOURS = 24
|
||
PORT_CACHE_MINUTES = 15
|
||
|
||
# =============================================================================
|
||
# DATABASE
|
||
# =============================================================================
|
||
|
||
PG_POOL_MIN_CONN = 1
|
||
PG_POOL_MAX_CONN = 5
|
||
|
||
# Position cleanup
|
||
POSITION_CLEANUP_INTERVAL_HOURS = 6
|
||
POSITION_KEEP_HOURS = 48
|
||
|
||
# =============================================================================
|
||
# EQUASIS
|
||
# =============================================================================
|
||
|
||
EQUASIS_DAILY_LIMIT = 400 # conservative (actual ~500)
|
||
|
||
EQUASIS_CACHE_HOURS = {
|
||
'search': 24, # search results: 24h
|
||
'details': 168, # vessel details: 7 days
|
||
'contacts': 168, # company contacts: 7 days
|
||
}
|
||
|
||
# =============================================================================
|
||
# WALLET / PAYMENTS (USDT TRC20)
|
||
# =============================================================================
|
||
|
||
USDT_TRC20_CONTRACT = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t'
|
||
DEPOSIT_FEE_PERCENT = 2.0
|
||
DEPOSIT_FEE_MIN = 0.10
|
||
WITHDRAW_FEE = 1.0
|
||
WITHDRAW_MIN = 2.0
|
||
|
||
# =============================================================================
|
||
# AGENT MEMORY
|
||
# =============================================================================
|
||
|
||
MEMORY_MAX_FACTS = 50 # max memories per user
|
||
MEMORY_INJECT_LIMIT = 10 # memories injected into prompt
|
||
MEMORY_SUMMARY_INTERVAL = 20 # summarize every N messages
|
||
MEMORY_DECAY_HALFLIFE_DAYS = 30 # half-life for relevance decay
|
||
|
||
# =============================================================================
|
||
# SECURITY
|
||
# =============================================================================
|
||
|
||
CHAT_MESSAGE_MAX_LENGTH = 2000 # max user message length (chars)
|
||
MEMORY_CONTENT_MAX_LENGTH = 200 # max single memory entry length
|
||
|
||
# =============================================================================
|
||
# SMART PARSE (Layer 2 — local entity extraction, 0 tokens)
|
||
# =============================================================================
|
||
|
||
SMART_PARSE_MAX_LENGTH = 200 # skip smart parse for messages longer than this
|
||
SMART_PARSE_FUZZY_THRESHOLD = 0.65 # minimum difflib score for fuzzy vessel match
|
||
PROFILE_CACHE_TTL = 60 # seconds — profile cache for fast path layers
|
||
|
||
# =============================================================================
|
||
# LIGHTWEIGHT AI EXTRACTION (Layer 3 — ~230 tokens per query)
|
||
# =============================================================================
|
||
|
||
LAYER3_EXTRACT_MAX_TOKENS = 150 # max tokens for AI extraction response
|
||
LAYER3_MAX_MESSAGE_LENGTH = 300 # skip Layer 3 for messages longer than this
|
||
|
||
|
||
# =============================================================================
|
||
# VESSEL COLLECTOR (background Med/Baltic/Caspian enrichment)
|
||
# =============================================================================
|
||
|
||
COLLECTOR_CYCLE_HOURS = 6 # run every 6 hours
|
||
COLLECTOR_MAX_PER_CYCLE = 40 # max vessels to enrich per cycle
|
||
COLLECTOR_EQUASIS_RESERVE = 50 # stop if equasis remaining < this
|
||
COLLECTOR_ENRICHMENT_TTL_DAYS = 7 # re-enrich after 7 days
|
||
COLLECTOR_MIN_TYPE_CODE = 70 # only cargo (70+) and tanker (80+)
|
||
|
||
# Freight-only filter (temporary — set False to show all vessel types)
|
||
FREIGHT_VESSELS_ONLY = True
|
||
FREIGHT_MIN_TYPE_CODE = 70 # cargo (70-79) + tanker (80-89)
|
||
|
||
# Vessel type category codes (for compact bulk API response)
|
||
TYPE_CAT_CODES = {
|
||
'tanker': 0, 'bulk': 1, 'container': 2, 'cargo': 3,
|
||
'passenger': 4, 'roro': 5, 'fishing': 6, 'tug': 7,
|
||
'offshore': 8, 'other': 9
|
||
}
|
||
TYPE_CAT_NAMES = {v: k for k, v in TYPE_CAT_CODES.items()}
|
||
|
||
# =============================================================================
|
||
# ENVIRONMENT VARIABLE NAMES
|
||
# =============================================================================
|
||
|
||
ENV_ANTHROPIC_API_KEY = 'ANTHROPIC_API_KEY'
|
||
ENV_SECRET_KEY = 'SECRET_KEY'
|
||
ENV_GOOGLE_CLIENT_ID = 'GOOGLE_CLIENT_ID'
|
||
ENV_ADMIN_EMAILS = 'ADMIN_EMAILS'
|
||
ENV_DATABASE_URL = 'DATABASE_URL'
|
||
ENV_MOLTBOOK_API_KEY = 'MOLTBOOK_API_KEY'
|
||
ENV_AISSTREAM_API_KEY = 'AISSTREAM_API_KEY'
|
||
ENV_AISHUB_USERNAME = 'AISHUB_USERNAME'
|
||
ENV_DIGITRAFFIC_ENABLED = 'DIGITRAFFIC_ENABLED'
|
||
ENV_RENDER_EXTERNAL_URL = 'RENDER_EXTERNAL_URL'
|
||
ENV_SEAFARE_PORT = 'SEAFARE_PORT'
|
||
ENV_FLASK_DEBUG = 'FLASK_DEBUG'
|
||
|
||
|
||
# ==================================================================
|
||
# ROLE-BASED SYSTEM (v3.39.0)
|
||
# ==================================================================
|
||
|
||
ROLE_GROUPS = {
|
||
"fleet": ["shipowner", "operator"],
|
||
"cargo": ["charterer", "freight_forwarder"],
|
||
"market": ["broker", "port_agent", "surveyor"],
|
||
}
|
||
|
||
ROLE_PROMPT_ADDONS = {
|
||
"fleet": (
|
||
"User is a SHIPOWNER/OPERATOR. They likely ask about THEIR OWN vessels. "
|
||
"Check vessels_of_interest first for disambiguation. "
|
||
"Focus on: fleet tracking, cargo matching for their vessels, contacts of charterers, route optimization. "
|
||
"Style: technical maritime terminology, concise operational data."
|
||
),
|
||
"cargo": (
|
||
"User is a CARGO OWNER/CHARTERER. They need to SHIP cargo. "
|
||
"Focus on: finding available vessels, freight estimates, route/time, vessel vetting, owner contacts. "
|
||
"Style: business-oriented, cost comparisons, delivery timelines."
|
||
),
|
||
"market": (
|
||
"User is a BROKER/AGENT. They match cargo to vessels. "
|
||
"Focus on: market intelligence, vessel availability by region, owner/operator contacts, tonnage specs. "
|
||
"Style: professional, data-dense, comparative tables."
|
||
),
|
||
}
|
||
|
||
OUT_OF_SCOPE_KEYWORDS = {
|
||
"demurrage": ["демередж", "demurrage", "demora", "простой судна", "штраф за простой", "laytime"],
|
||
"bunker": ["бункер", "bunker", "vlsfo", "hsfo", "mgo", "fuel price", "цена топлив"],
|
||
"sanctions": ["санкци", "санкции", "sanctions", "sanciones", "ofac", "screening", "скрининг"],
|
||
"charter_party": ["чартер-парти", "charter party", "carta de fletamento", "тайм-чартер"],
|
||
"bill_of_lading": ["коносамент", "bill of lading", "conocimiento de embarque"],
|
||
"fixture_recap": ["fixture recap", "фикстура", "рекап"],
|
||
"insurance": ["страхов", "insurance", "seguro", "p&i club", "hull insurance"],
|
||
"port_costs": ["портовые расход", "port cost", "costos portuarios", "лоцман", "pilotage", "remolcador"],
|
||
"congestion": ["загруженност", "congestion", "пробка в порту"],
|
||
"weather": ["погод", "weather", "clima", "шторм", "forecast", "pronostico", "tiempo"],
|
||
"crew": ["экипаж", "crew change", "manning", "смена экипажа"],
|
||
"performance": ["cii рейтинг", "cii rating", "vessel performance", "производительн", "расход топлив"],
|
||
"dark_fleet": ["dark fleet", "теневой флот", "тёмн", "flota oscura", "shadow fleet"],
|
||
"ais_anomaly": ["ais аномали", "ais anomal", "spoofing", "спуфинг"],
|
||
"freight_rate": ["ставка фрахт", "freight rate", "tasa de flete", "bdi", "baltic dry", "baltic index", "стоит фрахт", "цена фрахт", "фрахтовая ставка", "cost of freight"],
|
||
"laycan": ["laycan", "лейкан", "lay can", "lay/can"],
|
||
"laytime": ["laytime", "лейтайм", "lay time", "loading rate", "норма погрузки", "discharge rate", "норма выгрузки"],
|
||
"tce": ["tce", "time charter equivalent", "тайм-чартерный эквивалент"],
|
||
"port_dues": ["port dues", "портовые сбор", "terminal handling", "thc", "причальный сбор"],
|
||
}
|
||
|
||
OUT_OF_SCOPE_RESPONSE = {
|
||
"en": (
|
||
"This is outside our current service scope. "
|
||
"We specialize in: **vessel tracking & search**, **route calculation**, "
|
||
"**cargo-to-vessel matching**, and **owner/operator contacts**. "
|
||
"How can I help you with these?"
|
||
),
|
||
"zh": (
|
||
"这超出了我们当前的服务范围。"
|
||
"我们专注于:**船舶追踪与搜索**、**航线计算**、"
|
||
"**货物匹配船舶** 和 **船主/运营商联系人**。"
|
||
"我能在这些方面为您提供帮助吗?"
|
||
),
|
||
"es": (
|
||
"Esto está fuera de nuestros servicios actuales. "
|
||
"Nos especializamos en: **rastreo de buques**, **cálculo de rutas**, "
|
||
"**selección de buques para carga** y **contactos de armadores/operadores**. "
|
||
"¿Cómo puedo ayudarle con estos servicios?"
|
||
),
|
||
"ru": (
|
||
"Это за рамками наших текущих сервисов. "
|
||
"Мы специализируемся на: **отслеживание судов**, **расчёт маршрутов**, "
|
||
"**подбор судов под груз** и **контакты владельцев/операторов**. "
|
||
"Чем могу помочь в этих направлениях?"
|
||
),
|
||
}
|