""" 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": ( "Это за рамками наших текущих сервисов. " "Мы специализируемся на: **отслеживание судов**, **расчёт маршрутов**, " "**подбор судов под груз** и **контакты владельцев/операторов**. " "Чем могу помочь в этих направлениях?" ), }