fix(models): gemini-2.5-flash (light) + gemini-2.5-pro (heavy), LOCATION=global for modellkall

This commit is contained in:
chrischristiansen-glitch 2026-05-26 01:37:28 +02:00
parent f711f7be74
commit 7585a485e0
2 changed files with 35 additions and 39 deletions

View File

@ -1,16 +1,17 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
agent.py Cloud Run entrypoint for OSVauco OPAX agent. agent.py OSVauco OPAX agent.
Modes: Modes:
light (standard) gemini-2.0-flash-001, $1/task hard stop light gemini-2.5-flash, $1/task
heavy (audit+) gemini-2.5-pro-001, $3/task hard stop heavy gemini-2.5-pro, $3/task
Legacy values 'A' and 'A+' are accepted and normalized. Legacy 'A' / 'A+' normaliseres automatisk.
Authorized users for heavy: opax, admin Authoriserte brukere for heavy: opax, admin
Requires: google-adk >= 1.0.0,<2.0.0 Gjeldende modell-tilgjengelighet (mai 2026):
google-cloud-aiplatform >= 1.112.0 gemini-2.0-flash-001 kun for eksisterende kunder bruk IKKE.
gemini-2.5-flash / gemini-2.5-pro krever GOOGLE_CLOUD_LOCATION=global.
""" """
import asyncio import asyncio
@ -26,18 +27,18 @@ from google.genai import types
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
PROJECT_ID = os.environ.get("GOOGLE_CLOUD_PROJECT", "propane-will-491900-m5") PROJECT_ID = os.environ.get("GOOGLE_CLOUD_PROJECT", "propane-will-491900-m5")
LOCATION = os.environ.get("GOOGLE_CLOUD_LOCATION", "us-central1") LOCATION = os.environ.get("GOOGLE_CLOUD_LOCATION", "global")
RAG_CORPUS = os.environ.get("RAG_CORPUS", "") RAG_CORPUS = os.environ.get("RAG_CORPUS", "")
# Light mode — pinned to stable versioned alias # Light mode — gemini-2.5-flash (GA, global)
ORCHESTRATOR_MODEL = os.environ.get("ORCHESTRATOR_MODEL", "gemini-2.0-flash-001") ORCHESTRATOR_MODEL = os.environ.get("ORCHESTRATOR_MODEL", "gemini-2.5-flash")
SUBAGENT_MODEL = os.environ.get("SUBAGENT_MODEL", "gemini-2.0-flash-001") SUBAGENT_MODEL = os.environ.get("SUBAGENT_MODEL", "gemini-2.5-flash")
REASONING_MODEL = os.environ.get("REASONING_MODEL", "gemini-2.0-flash-001") REASONING_MODEL = os.environ.get("REASONING_MODEL", "gemini-2.5-flash")
# Heavy mode # Heavy mode — gemini-2.5-pro (GA, global)
HEAVY_ORCHESTRATOR = os.environ.get("HEAVY_ORCHESTRATOR_MODEL", "gemini-2.5-pro-001") HEAVY_ORCHESTRATOR = os.environ.get("HEAVY_ORCHESTRATOR_MODEL", "gemini-2.5-pro")
HEAVY_SUBAGENT = os.environ.get("HEAVY_SUBAGENT_MODEL", "gemini-2.5-pro-001") HEAVY_SUBAGENT = os.environ.get("HEAVY_SUBAGENT_MODEL", "gemini-2.5-pro")
HEAVY_REASONING = os.environ.get("HEAVY_REASONING_MODEL", "gemini-2.5-flash-001") HEAVY_REASONING = os.environ.get("HEAVY_REASONING_MODEL", "gemini-2.5-flash")
BUDGET_LIGHT = float(os.environ.get("BUDGET_A_USD_PER_TASK", "1.0")) BUDGET_LIGHT = float(os.environ.get("BUDGET_A_USD_PER_TASK", "1.0"))
BUDGET_HEAVY = float(os.environ.get("HEAVY_MODE_BUDGET_USD_PER_DAY", "3.0")) BUDGET_HEAVY = float(os.environ.get("HEAVY_MODE_BUDGET_USD_PER_DAY", "3.0"))
@ -59,8 +60,7 @@ def _normalize_mode(mode: str) -> str:
def authorize_mode(user_id: str, mode: str) -> None: def authorize_mode(user_id: str, mode: str) -> None:
"""Normalize mode first, then check authorization. Raises ValueError or PermissionError.""" mode = _normalize_mode(mode)
mode = _normalize_mode(mode) # raises ValueError for unknown modes
if mode == "heavy" and user_id not in HEAVY_MODE_ALLOWED_USERS: if mode == "heavy" and user_id not in HEAVY_MODE_ALLOWED_USERS:
raise PermissionError( raise PermissionError(
f"User '{user_id}' is not authorized for heavy mode. " f"User '{user_id}' is not authorized for heavy mode. "
@ -80,11 +80,11 @@ if RAG_CORPUS:
similarity_top_k=10, similarity_top_k=10,
vector_distance_threshold=0.6, vector_distance_threshold=0.6,
) )
logger.info(f"RAG tool initialised with corpus: {RAG_CORPUS}") logger.info(f"RAG tool initialised: {RAG_CORPUS}")
except ImportError as e: except ImportError as e:
logger.warning(f"VertexAiRagRetrieval not available — running without RAG: {e}") logger.warning(f"VertexAiRagRetrieval ikke tilgjengelig: {e}")
else: else:
logger.warning("RAG_CORPUS env var not set — running without RAG retrieval") logger.warning("RAG_CORPUS ikke satt — kjører uten RAG")
def get_models_for_mode(mode: Mode) -> dict: def get_models_for_mode(mode: Mode) -> dict:
@ -108,18 +108,16 @@ def get_models_for_mode(mode: Mode) -> dict:
def build_agent(mode: str = "light") -> Agent: def build_agent(mode: str = "light") -> Agent:
mode = _normalize_mode(mode) mode = _normalize_mode(mode)
models = get_models_for_mode(mode) models = get_models_for_mode(mode)
mode_label = "heavy" if mode == "heavy" else "light"
instruction = ( instruction = (
f"Du er OPAX — OSVauco AI-agent. " f"Du er OPAX — OSVauco AI-agent. Modus: {mode}. "
f"Modus: {mode_label}. Budsjettgrense per oppgave: ${models['budget_usd']}. " f"Budsjettgrense per oppgave: ${models['budget_usd']}. "
"Bruk retrieve_knowledge-verktøyet for å hente dokumentasjon og kontekst. " "Bruk retrieve_knowledge for å hente dokumentasjon. "
"Foretrekk alltid dokumenterte svar fremfor spekulasjon. " "Svar på norsk (bokmål) med mindre annet er bedt om."
"Svar alltid på norsk (bokmål) med mindre brukeren eksplisitt ber om et annet språk."
) )
return Agent( return Agent(
model=models["orchestrator"], model=models["orchestrator"],
name="opax_agent", name="opax_agent",
description=f"OPAX — OSVauco enterprise agent [{mode_label}]", description=f"OPAX — OSVauco enterprise agent [{mode}]",
instruction=instruction, instruction=instruction,
tools=[rag_tool] if rag_tool else [], tools=[rag_tool] if rag_tool else [],
) )
@ -162,9 +160,8 @@ def run(
if __name__ == "__main__": if __name__ == "__main__":
import sys import sys
logging.basicConfig(level=logging.WARNING) logging.basicConfig(level=logging.WARNING)
query = sys.argv[1] if len(sys.argv) > 1 else "Hva er OPAX heavy mode?" query = sys.argv[1] if len(sys.argv) > 1 else "Hva er OPAX?"
requested_mode = sys.argv[2] if len(sys.argv) > 2 else "light" mode = sys.argv[2] if len(sys.argv) > 2 else "light"
print(f"Mode : {requested_mode}") print(f"Mode: {mode} | Query: {query}")
print(f"Query: {query}")
print("-" * 60) print("-" * 60)
print(run(message=query, user_id="opax", mode=requested_mode)) print(run(message=query, user_id="opax", mode=mode))

11
main.py
View File

@ -1,8 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
main.py Cloud Run entrypoint for OSVauco OPAX agent. main.py Cloud Run entrypoint, OSVauco OPAX.
Modes: light (default, gemini-2.0-flash-001) | heavy (gemini-2.5-pro-001) Modes: light (gemini-2.5-flash) | heavy (gemini-2.5-pro)
ML-1: telemetri, state store, DAG-endepunkter. ML-1: telemetri, state store, DAG.
""" """
import os import os
@ -31,7 +31,7 @@ class RunRequest(BaseModel):
class DagRequest(BaseModel): class DagRequest(BaseModel):
messages: List[str] = Field(..., description="Liste av meldinger som kjøres parallelt") messages: List[str] = Field(...)
user_id: str = "opax" user_id: str = "opax"
session_id: str = "default" session_id: str = "default"
mode: str = "light" mode: str = "light"
@ -45,7 +45,6 @@ def health():
@app.post("/run") @app.post("/run")
def run_agent(req: RunRequest): def run_agent(req: RunRequest):
# Validate mode and authorization before any work — returns 400/403 on bad input
try: try:
authorize_mode(req.user_id, req.mode) authorize_mode(req.user_id, req.mode)
except ValueError as e: except ValueError as e:
@ -71,7 +70,7 @@ def run_agent(req: RunRequest):
finally: finally:
duration = round(time.monotonic() - start, 3) duration = round(time.monotonic() - start, 3)
success = error_msg is None success = error_msg is None
model = "gemini-2.0-flash-001" if req.mode in ("light", "A") else "gemini-2.5-pro-001" model = "gemini-2.5-flash" if req.mode in ("light", "A") else "gemini-2.5-pro"
log_agent_call( log_agent_call(
agent_id=AGENT_ID, agent_id=AGENT_ID,
input_payload={"message": req.message, "mode": req.mode}, input_payload={"message": req.message, "mode": req.mode},