Refactor app.py for logging and configuration updates
Refactor app.py to improve logging, update configuration paths, and enhance session management.
This commit is contained in:
@@ -6,8 +6,9 @@
|
|||||||
# export API_KEY="minha-chave"
|
# export API_KEY="minha-chave"
|
||||||
# export GENAI_BUCKET="lohmann-ai-br"
|
# export GENAI_BUCKET="lohmann-ai-br"
|
||||||
# export GENAI_UPLOAD_PREFIX="genai-uploads/"
|
# export GENAI_UPLOAD_PREFIX="genai-uploads/"
|
||||||
# export LLM_CONFIG_PATH="/home/app/llm_models.json"
|
# export OCI_CONFIG_FILE="./credentials.conf" # opcional, padrão: ./credentials.conf
|
||||||
# export DEBUG_AUTH=true # opcional
|
# export LLM_CONFIG_PATH="./llm_models.json" # opcional, padrão: ./llm_models.json
|
||||||
|
# export DEBUG_AUTH=true # opcional
|
||||||
# python app.py # porta 8000
|
# python app.py # porta 8000
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -22,8 +23,17 @@ import base64
|
|||||||
import time
|
import time
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import hmac
|
import hmac
|
||||||
|
import logging
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from typing import Any, Dict, List, Optional, Generator
|
from typing import Any, Dict, List, Optional, Generator
|
||||||
|
from functools import lru_cache, wraps
|
||||||
|
|
||||||
|
# Configurar logging
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||||
|
)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
@@ -42,7 +52,7 @@ try:
|
|||||||
methods=["GET", "POST", "OPTIONS"]
|
methods=["GET", "POST", "OPTIONS"]
|
||||||
)
|
)
|
||||||
except Exception as _e:
|
except Exception as _e:
|
||||||
print("AVISO: flask-cors não instalado; CORS mínimo será aplicado via after_request.")
|
logger.warning("flask-cors não instalado; CORS mínimo será aplicado via after_request.")
|
||||||
|
|
||||||
@app.after_request
|
@app.after_request
|
||||||
def add_cors_headers(resp):
|
def add_cors_headers(resp):
|
||||||
@@ -51,11 +61,35 @@ def add_cors_headers(resp):
|
|||||||
resp.headers.setdefault("Access-Control-Allow-Headers", "Content-Type, Authorization, X-API-Key, X-Channel, X-Cuid")
|
resp.headers.setdefault("Access-Control-Allow-Headers", "Content-Type, Authorization, X-API-Key, X-Channel, X-Cuid")
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
# ==========================
|
||||||
|
# Constantes de Parâmetros de Modelo
|
||||||
|
# ==========================
|
||||||
|
|
||||||
|
# Parâmetros de modelo com mapeamento 1:1 (sem transformação)
|
||||||
|
SIMPLE_MODEL_PARAMS = [
|
||||||
|
"temperature",
|
||||||
|
"top_p",
|
||||||
|
"top_k",
|
||||||
|
"frequency_penalty",
|
||||||
|
"presence_penalty",
|
||||||
|
"reasoning_effort",
|
||||||
|
"verbosity"
|
||||||
|
]
|
||||||
|
|
||||||
# ==========================
|
# ==========================
|
||||||
# Configuração e Autenticação OCI
|
# Configuração e Autenticação OCI
|
||||||
# ==========================
|
# ==========================
|
||||||
|
|
||||||
def load_config(config_file="/home/app/credentials.conf"):
|
def load_config(config_file=None):
|
||||||
|
"""Carrega configuração OCI de arquivo.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config_file: Caminho do arquivo de configuração. Se None, usa variável de ambiente OCI_CONFIG_FILE
|
||||||
|
ou padrão './credentials.conf'
|
||||||
|
"""
|
||||||
|
if config_file is None:
|
||||||
|
config_file = os.environ.get("OCI_CONFIG_FILE", "./credentials.conf")
|
||||||
|
|
||||||
config = {}
|
config = {}
|
||||||
try:
|
try:
|
||||||
with open(config_file, 'r') as f:
|
with open(config_file, 'r') as f:
|
||||||
@@ -85,8 +119,8 @@ if not TEST_MODE:
|
|||||||
private_key_content=config.get("key_content"),
|
private_key_content=config.get("key_content"),
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Erro ao inicializar signer OCI: {e}")
|
logger.error(f"Erro ao inicializar signer OCI: {e}")
|
||||||
print("Executando em modo de teste...")
|
logger.info("Executando em modo de teste...")
|
||||||
TEST_MODE = True
|
TEST_MODE = True
|
||||||
|
|
||||||
# ==========================
|
# ==========================
|
||||||
@@ -111,7 +145,7 @@ def _parse_bearer_token(auth_header: str) -> str:
|
|||||||
def check_api_key():
|
def check_api_key():
|
||||||
expected_key = os.environ.get("API_KEY")
|
expected_key = os.environ.get("API_KEY")
|
||||||
if not expected_key:
|
if not expected_key:
|
||||||
print("AVISO: API_KEY não configurada nas variáveis de ambiente.")
|
logger.warning("API_KEY não configurada nas variáveis de ambiente.")
|
||||||
return
|
return
|
||||||
|
|
||||||
provided_key = request.headers.get("X-API-Key")
|
provided_key = request.headers.get("X-API-Key")
|
||||||
@@ -119,7 +153,7 @@ def check_api_key():
|
|||||||
bearer_token = _parse_bearer_token(auth_header)
|
bearer_token = _parse_bearer_token(auth_header)
|
||||||
|
|
||||||
if DEBUG_AUTH:
|
if DEBUG_AUTH:
|
||||||
print(f"[auth] method={request.method} path={request.path} "
|
logger.debug(f"[auth] method={request.method} path={request.path} "
|
||||||
f"X-API-Key={'<set>' if provided_key else '<none>'} "
|
f"X-API-Key={'<set>' if provided_key else '<none>'} "
|
||||||
f"Authorization={'<set>' if auth_header else '<none>'}")
|
f"Authorization={'<set>' if auth_header else '<none>'}")
|
||||||
|
|
||||||
@@ -152,11 +186,26 @@ if not TEST_MODE:
|
|||||||
object_client = oci.object_storage.ObjectStorageClient(config)
|
object_client = oci.object_storage.ObjectStorageClient(config)
|
||||||
namespace = object_client.get_namespace().data
|
namespace = object_client.get_namespace().data
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Erro ao inicializar ObjectStorageClient: {e}")
|
logger.error(f"Erro ao inicializar ObjectStorageClient: {e}")
|
||||||
TEST_MODE = True
|
TEST_MODE = True
|
||||||
|
|
||||||
FILE_INDEX: Dict[str, str] = {}
|
FILE_INDEX: Dict[str, str] = {}
|
||||||
|
|
||||||
|
# ==========================
|
||||||
|
# Cache de Clientes OCI
|
||||||
|
# ==========================
|
||||||
|
|
||||||
|
@lru_cache(maxsize=10)
|
||||||
|
def get_oci_inference_client(region: str) -> 'oci.generative_ai_inference.GenerativeAiInferenceClient':
|
||||||
|
"""Retorna cliente OCI GenAI Inference com cache"""
|
||||||
|
endpoint = f"https://inference.generativeai.{region}.oci.oraclecloud.com"
|
||||||
|
return oci.generative_ai_inference.GenerativeAiInferenceClient(
|
||||||
|
config=config,
|
||||||
|
service_endpoint=endpoint,
|
||||||
|
retry_strategy=oci.retry.NoneRetryStrategy(),
|
||||||
|
timeout=(10, 240)
|
||||||
|
)
|
||||||
|
|
||||||
# ==========================
|
# ==========================
|
||||||
# Helpers: Signed URL (PAR) + Upload
|
# Helpers: Signed URL (PAR) + Upload
|
||||||
# ==========================
|
# ==========================
|
||||||
@@ -214,7 +263,7 @@ def upload_file_to_bucket(file_storage, filename: str) -> Dict[str, Any]:
|
|||||||
# Modelos — JSON externo (hot-reload)
|
# Modelos — JSON externo (hot-reload)
|
||||||
# ==========================
|
# ==========================
|
||||||
|
|
||||||
LLM_CONFIG_PATH = os.environ.get("LLM_CONFIG_PATH", "/home/app/llm_models.json")
|
LLM_CONFIG_PATH = os.environ.get("LLM_CONFIG_PATH", "./llm_models.json")
|
||||||
|
|
||||||
SUPPORTED_MODELS_DEFAULTS: Dict[str, Dict[str, Any]] = {
|
SUPPORTED_MODELS_DEFAULTS: Dict[str, Dict[str, Any]] = {
|
||||||
"gpt5": {
|
"gpt5": {
|
||||||
@@ -244,7 +293,7 @@ def get_supported_models() -> Dict[str, Dict[str, Any]]:
|
|||||||
raise ValueError("Arquivo de modelos não contém 'models' válidos.")
|
raise ValueError("Arquivo de modelos não contém 'models' válidos.")
|
||||||
return valid
|
return valid
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[warn] Usando SUPPORTED_MODELS_DEFAULTS (motivo: {e})")
|
logger.warning(f"Usando SUPPORTED_MODELS_DEFAULTS (motivo: {e})")
|
||||||
return SUPPORTED_MODELS_DEFAULTS
|
return SUPPORTED_MODELS_DEFAULTS
|
||||||
|
|
||||||
def get_model_config(model_name: str) -> Dict[str, Any]:
|
def get_model_config(model_name: str) -> Dict[str, Any]:
|
||||||
@@ -278,7 +327,7 @@ def session_controller(region, agent_endpoint_id, channel, cuid):
|
|||||||
SESSION_STORE[session_key] = {
|
SESSION_STORE[session_key] = {
|
||||||
"sessionId": new_session_id, "createdAt": now, "lastUsedAt": now, "sessionKey": session_key
|
"sessionId": new_session_id, "createdAt": now, "lastUsedAt": now, "sessionKey": session_key
|
||||||
}
|
}
|
||||||
print(f"[agent] nova sessão criada (TEST): key={session_key} id={new_session_id}")
|
logger.info(f"[agent] nova sessão criada (TEST): key={session_key} id={new_session_id}")
|
||||||
return {"id": new_session_id, "sessionKey": session_key, "reused": False}
|
return {"id": new_session_id, "sessionKey": session_key, "reused": False}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -300,7 +349,7 @@ def session_controller(region, agent_endpoint_id, channel, cuid):
|
|||||||
SESSION_STORE[session_key] = {
|
SESSION_STORE[session_key] = {
|
||||||
"sessionId": data.get("id"), "createdAt": now, "lastUsedAt": now, "sessionKey": session_key
|
"sessionId": data.get("id"), "createdAt": now, "lastUsedAt": now, "sessionKey": session_key
|
||||||
}
|
}
|
||||||
print(f"[agent] nova sessão criada: key={session_key} id={data.get('id')}")
|
logger.info(f"[agent] nova sessão criada: key={session_key} id={data.get('id')}")
|
||||||
data["sessionKey"] = session_key
|
data["sessionKey"] = session_key
|
||||||
data["reused"] = False
|
data["reused"] = False
|
||||||
return data
|
return data
|
||||||
@@ -311,7 +360,7 @@ def _invalidate_session(session_key: str):
|
|||||||
try:
|
try:
|
||||||
if session_key in SESSION_STORE:
|
if session_key in SESSION_STORE:
|
||||||
del SESSION_STORE[session_key]
|
del SESSION_STORE[session_key]
|
||||||
print(f"[agent] sessão invalidada: key={session_key}")
|
logger.info(f"[agent] sessão invalidada: key={session_key}")
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -365,7 +414,7 @@ def ensure_data_url(image_url: str) -> str:
|
|||||||
b64 = base64.b64encode(content).decode("utf-8")
|
b64 = base64.b64encode(content).decode("utf-8")
|
||||||
return f"data:{mime};base64,{b64}"
|
return f"data:{mime};base64,{b64}"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[warn] Falha ao baixar imagem '{image_url}': {e}")
|
logger.warning(f"Falha ao baixar imagem '{image_url}': {e}")
|
||||||
return image_url
|
return image_url
|
||||||
|
|
||||||
def to_oci_messages(openai_messages: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
def to_oci_messages(openai_messages: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
||||||
@@ -402,24 +451,18 @@ def to_oci_messages(openai_messages: List[Dict[str, Any]]) -> List[Dict[str, Any
|
|||||||
def build_oci_chat_payload(messages: List[Dict[str, Any]], params: Dict[str, Any]) -> Dict[str, Any]:
|
def build_oci_chat_payload(messages: List[Dict[str, Any]], params: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Constrói payload para OCI Chat API"""
|
"""Constrói payload para OCI Chat API"""
|
||||||
payload = {"messages": messages}
|
payload = {"messages": messages}
|
||||||
if "temperature" in params:
|
|
||||||
payload["temperature"] = params["temperature"]
|
# Parâmetros simples (mapeamento 1:1)
|
||||||
if "top_p" in params:
|
for param in SIMPLE_MODEL_PARAMS:
|
||||||
payload["top_p"] = params["top_p"]
|
if param in params:
|
||||||
if "top_k" in params:
|
payload[param] = params[param]
|
||||||
payload["top_k"] = params["top_k"]
|
|
||||||
if "frequency_penalty" in params:
|
# Tratamento especial: max_tokens → max_completion_tokens (compatibilidade OpenAI)
|
||||||
payload["frequency_penalty"] = params["frequency_penalty"]
|
|
||||||
if "presence_penalty" in params:
|
|
||||||
payload["presence_penalty"] = params["presence_penalty"]
|
|
||||||
if "max_completion_tokens" in params:
|
if "max_completion_tokens" in params:
|
||||||
payload["max_completion_tokens"] = params["max_completion_tokens"]
|
payload["max_completion_tokens"] = params["max_completion_tokens"]
|
||||||
elif "max_tokens" in params:
|
elif "max_tokens" in params:
|
||||||
payload["max_completion_tokens"] = params["max_tokens"]
|
payload["max_completion_tokens"] = params["max_tokens"]
|
||||||
if "reasoning_effort" in params:
|
|
||||||
payload["reasoning_effort"] = params["reasoning_effort"]
|
|
||||||
if "verbosity" in params:
|
|
||||||
payload["verbosity"] = params["verbosity"]
|
|
||||||
return payload
|
return payload
|
||||||
|
|
||||||
def extract_token_usage(oci_response: Any) -> Dict[str, Optional[int]]:
|
def extract_token_usage(oci_response: Any) -> Dict[str, Optional[int]]:
|
||||||
@@ -449,14 +492,91 @@ def extract_token_usage(oci_response: Any) -> Dict[str, Optional[int]]:
|
|||||||
if usage["total_tokens"] is None and usage["prompt_tokens"] and usage["completion_tokens"]:
|
if usage["total_tokens"] is None and usage["prompt_tokens"] and usage["completion_tokens"]:
|
||||||
usage["total_tokens"] = usage["prompt_tokens"] + usage["completion_tokens"]
|
usage["total_tokens"] = usage["prompt_tokens"] + usage["completion_tokens"]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[warn] Erro ao extrair token usage: {e}")
|
logger.warning(f"Erro ao extrair token usage: {e}")
|
||||||
|
|
||||||
|
return usage
|
||||||
|
|
||||||
|
def extract_agent_token_usage(agent_response):
|
||||||
|
"""
|
||||||
|
Extrai informações de token usage de uma resposta de agente OCI.
|
||||||
|
Suporta múltiplas etapas de tool calling.
|
||||||
|
|
||||||
|
Estrutura esperada:
|
||||||
|
{
|
||||||
|
"traces": [
|
||||||
|
{
|
||||||
|
"traceType": "GENERATION_TRACE",
|
||||||
|
"usage": [
|
||||||
|
{
|
||||||
|
"usageDetails": {
|
||||||
|
"inputTokenCount": int,
|
||||||
|
"outputTokenCount": int
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Args:
|
||||||
|
agent_response: Resposta do agente (dict)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: {"prompt_tokens": int, "completion_tokens": int, "total_tokens": int}
|
||||||
|
"""
|
||||||
|
usage = {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0}
|
||||||
|
|
||||||
|
if not agent_response or not isinstance(agent_response, dict):
|
||||||
|
return usage
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Obter traces
|
||||||
|
traces = agent_response.get('traces', [])
|
||||||
|
|
||||||
|
total_input_tokens = 0
|
||||||
|
total_output_tokens = 0
|
||||||
|
|
||||||
|
# Iterar por todos os traces
|
||||||
|
for trace in traces:
|
||||||
|
# Verificar se é um GENERATION_TRACE (pode vir como traceType ou trace_type)
|
||||||
|
trace_type = trace.get('traceType') or trace.get('trace_type', '')
|
||||||
|
|
||||||
|
if trace_type == 'GENERATION_TRACE':
|
||||||
|
# Obter lista de usage
|
||||||
|
usage_list = trace.get('usage', [])
|
||||||
|
|
||||||
|
# Iterar por cada entrada de usage
|
||||||
|
for usage_entry in usage_list:
|
||||||
|
# Obter usageDetails (pode vir como usageDetails ou usage_details)
|
||||||
|
usage_details = usage_entry.get('usageDetails') or usage_entry.get('usage_details', {})
|
||||||
|
|
||||||
|
# Extrair contagens (pode vir em camelCase ou snake_case)
|
||||||
|
input_tokens = (
|
||||||
|
usage_details.get('inputTokenCount') or
|
||||||
|
usage_details.get('input_token_count', 0)
|
||||||
|
)
|
||||||
|
output_tokens = (
|
||||||
|
usage_details.get('outputTokenCount') or
|
||||||
|
usage_details.get('output_token_count', 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
total_input_tokens += input_tokens
|
||||||
|
total_output_tokens += output_tokens
|
||||||
|
|
||||||
|
# Atualizar usage com os totais
|
||||||
|
usage["prompt_tokens"] = total_input_tokens
|
||||||
|
usage["completion_tokens"] = total_output_tokens
|
||||||
|
usage["total_tokens"] = total_input_tokens + total_output_tokens
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Erro ao extrair token usage de agente: {e}")
|
||||||
|
|
||||||
return usage
|
return usage
|
||||||
|
|
||||||
def oci_chat_invoke(model_region: str, compartment_id: str, model_ocid: str, oci_payload: Dict[str, Any]) -> Dict[str, Any]:
|
def oci_chat_invoke(model_region: str, compartment_id: str, model_ocid: str, oci_payload: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Invoca modelo OCI GenAI e retorna resposta com token usage"""
|
"""Invoca modelo OCI GenAI e retorna resposta com token usage"""
|
||||||
print(">>> OCI CHAT REQUEST (payload que será enviado):")
|
logger.debug(">>> OCI CHAT REQUEST (payload que será enviado):")
|
||||||
print(json.dumps(oci_payload, ensure_ascii=False, indent=2))
|
logger.debug(json.dumps(oci_payload, ensure_ascii=False, indent=2))
|
||||||
|
|
||||||
if TEST_MODE:
|
if TEST_MODE:
|
||||||
return {
|
return {
|
||||||
@@ -468,10 +588,7 @@ def oci_chat_invoke(model_region: str, compartment_id: str, model_ocid: str, oci
|
|||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
endpoint = f"https://inference.generativeai.{model_region}.oci.oraclecloud.com"
|
client = get_oci_inference_client(model_region)
|
||||||
client = oci.generative_ai_inference.GenerativeAiInferenceClient(
|
|
||||||
config=config, service_endpoint=endpoint, retry_strategy=oci.retry.NoneRetryStrategy(), timeout=(10, 240)
|
|
||||||
)
|
|
||||||
|
|
||||||
chat_detail = oci.generative_ai_inference.models.ChatDetails()
|
chat_detail = oci.generative_ai_inference.models.ChatDetails()
|
||||||
generic = oci.generative_ai_inference.models.GenericChatRequest()
|
generic = oci.generative_ai_inference.models.GenericChatRequest()
|
||||||
@@ -499,18 +616,14 @@ def oci_chat_invoke(model_region: str, compartment_id: str, model_ocid: str, oci
|
|||||||
|
|
||||||
generic.messages = sdk_messages
|
generic.messages = sdk_messages
|
||||||
|
|
||||||
if "temperature" in oci_payload:
|
# Parâmetros simples (mapeamento 1:1)
|
||||||
generic.temperature = oci_payload["temperature"]
|
for param in SIMPLE_MODEL_PARAMS:
|
||||||
if "top_p" in oci_payload:
|
if param in oci_payload:
|
||||||
generic.top_p = oci_payload["top_p"]
|
setattr(generic, param, oci_payload[param])
|
||||||
if "top_k" in oci_payload:
|
|
||||||
generic.top_k = oci_payload["top_k"]
|
# Tratamento especial: max_completion_tokens
|
||||||
if "frequency_penalty" in oci_payload:
|
|
||||||
generic.frequency_penalty = oci_payload["frequency_penalty"]
|
|
||||||
if "presence_penalty" in oci_payload:
|
|
||||||
generic.presence_penalty = oci_payload["presence_penalty"]
|
|
||||||
if "max_completion_tokens" in oci_payload:
|
if "max_completion_tokens" in oci_payload:
|
||||||
generic.max_tokens = oci_payload["max_completion_tokens"]
|
generic.max_completion_tokens = oci_payload["max_completion_tokens"]
|
||||||
|
|
||||||
chat_detail.serving_mode = oci.generative_ai_inference.models.OnDemandServingMode(model_id=model_ocid)
|
chat_detail.serving_mode = oci.generative_ai_inference.models.OnDemandServingMode(model_id=model_ocid)
|
||||||
chat_detail.chat_request = generic
|
chat_detail.chat_request = generic
|
||||||
@@ -705,6 +818,191 @@ def _extract_agent_text(agent_payload: Any) -> str:
|
|||||||
def test():
|
def test():
|
||||||
return jsonify({"test": "ok", "version": "2.0-refactored"})
|
return jsonify({"test": "ok", "version": "2.0-refactored"})
|
||||||
|
|
||||||
|
# ==========================
|
||||||
|
# Endpoints Globais OpenAI v1 (compatibilidade total com SDK OpenAI)
|
||||||
|
# ==========================
|
||||||
|
|
||||||
|
@app.route("/v1/models", methods=["GET"])
|
||||||
|
def list_all_models():
|
||||||
|
"""
|
||||||
|
Lista todos os modelos disponíveis.
|
||||||
|
Compatível com: OpenAI SDK client.models.list()
|
||||||
|
"""
|
||||||
|
supported = get_supported_models()
|
||||||
|
now = int(time.time())
|
||||||
|
models_list = []
|
||||||
|
|
||||||
|
for name, cfg in supported.items():
|
||||||
|
models_list.append({
|
||||||
|
"id": name,
|
||||||
|
"object": "model",
|
||||||
|
"created": now,
|
||||||
|
"owned_by": "oci.genai",
|
||||||
|
"permission": [],
|
||||||
|
"root": name,
|
||||||
|
"parent": None,
|
||||||
|
"type": cfg.get("type", "model"),
|
||||||
|
"region": cfg.get("region"),
|
||||||
|
"ocid": cfg.get("id"),
|
||||||
|
"compartmentId": cfg.get("compartmentId"),
|
||||||
|
"params": cfg.get("params", {})
|
||||||
|
})
|
||||||
|
|
||||||
|
return jsonify({"object": "list", "data": models_list})
|
||||||
|
|
||||||
|
@app.route("/v1/models/<model_id>", methods=["GET"])
|
||||||
|
def get_model_info(model_id):
|
||||||
|
"""
|
||||||
|
Retorna informações de um modelo específico.
|
||||||
|
Compatível com: OpenAI SDK client.models.retrieve(model_id)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
model_config = get_model_config(model_id)
|
||||||
|
return jsonify({
|
||||||
|
"id": model_id,
|
||||||
|
"object": "model",
|
||||||
|
"created": int(time.time()),
|
||||||
|
"owned_by": "oci.genai",
|
||||||
|
"permission": [],
|
||||||
|
"root": model_id,
|
||||||
|
"parent": None,
|
||||||
|
"ocid": model_config.get("id"),
|
||||||
|
"compartmentId": model_config.get("compartmentId"),
|
||||||
|
"region": model_config.get("region"),
|
||||||
|
"type": model_config.get("type", "model"),
|
||||||
|
"params": model_config.get("params", {})
|
||||||
|
})
|
||||||
|
except ValueError as e:
|
||||||
|
return jsonify({"error": {"message": str(e), "type": "invalid_request_error", "code": "model_not_found"}}), 404
|
||||||
|
|
||||||
|
@app.route("/v1/chat/completions", methods=["POST"])
|
||||||
|
def global_chat_completions():
|
||||||
|
"""
|
||||||
|
Chat completion global.
|
||||||
|
Compatível com: OpenAI SDK client.chat.completions.create()
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
body = request.get_json(force=True, silent=False) or {}
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"error": {"message": f"JSON inválido: {e}", "type": "invalid_request_error"}}), 400
|
||||||
|
|
||||||
|
model_name = body.get("model")
|
||||||
|
if not model_name:
|
||||||
|
return jsonify({"error": {"message": "Campo 'model' é obrigatório", "type": "invalid_request_error", "param": "model"}}), 400
|
||||||
|
|
||||||
|
try:
|
||||||
|
model_config = get_model_config(model_name)
|
||||||
|
except ValueError as e:
|
||||||
|
return jsonify({"error": {"message": str(e), "type": "invalid_request_error", "code": "model_not_found"}}), 404
|
||||||
|
|
||||||
|
# Redireciona para a lógica existente baseado no tipo
|
||||||
|
if model_config.get("type") == "agent":
|
||||||
|
return _handle_agent_chat(model_name, model_config, body)
|
||||||
|
|
||||||
|
# Lógica para modelos
|
||||||
|
msgs = body.get("messages") or []
|
||||||
|
if not isinstance(msgs, list) or not msgs:
|
||||||
|
return jsonify({"error": {"message": "Campo 'messages' é obrigatório e deve ser uma lista", "type": "invalid_request_error", "param": "messages"}}), 400
|
||||||
|
|
||||||
|
params = model_config.get("params", {}).copy()
|
||||||
|
for k in ["temperature", "top_p", "top_k", "max_tokens", "frequency_penalty",
|
||||||
|
"presence_penalty", "reasoning_effort", "verbosity", "max_completion_tokens"]:
|
||||||
|
if k in body and body[k] is not None:
|
||||||
|
params[k] = body[k]
|
||||||
|
|
||||||
|
oci_msgs = to_oci_messages(msgs)
|
||||||
|
oci_payload = build_oci_chat_payload(oci_msgs, params)
|
||||||
|
|
||||||
|
model_region = model_config.get("region")
|
||||||
|
compartment_id = model_config.get("compartmentId")
|
||||||
|
model_ocid = model_config.get("id")
|
||||||
|
|
||||||
|
oci_result = oci_chat_invoke(model_region, compartment_id, model_ocid, oci_payload)
|
||||||
|
|
||||||
|
if isinstance(oci_result, dict):
|
||||||
|
output_text = oci_result.get("output_text")
|
||||||
|
usage = oci_result.get("usage", {})
|
||||||
|
else:
|
||||||
|
output_text = None
|
||||||
|
usage = {}
|
||||||
|
|
||||||
|
if output_text is None:
|
||||||
|
output_text = json.dumps(oci_result, ensure_ascii=False)
|
||||||
|
|
||||||
|
if body.get("stream") is True:
|
||||||
|
return Response(
|
||||||
|
stream_with_context(sse_chat_stream(model_name, output_text)),
|
||||||
|
mimetype="text/event-stream"
|
||||||
|
)
|
||||||
|
|
||||||
|
return jsonify(to_openai_chat_response(model_name, output_text, usage))
|
||||||
|
|
||||||
|
@app.route("/v1/completions", methods=["POST"])
|
||||||
|
def global_text_completions():
|
||||||
|
"""
|
||||||
|
Text completion global.
|
||||||
|
Compatível com: OpenAI SDK client.completions.create()
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
body = request.get_json(force=True, silent=False) or {}
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"error": {"message": f"JSON inválido: {e}", "type": "invalid_request_error"}}), 400
|
||||||
|
|
||||||
|
model_name = body.get("model")
|
||||||
|
if not model_name:
|
||||||
|
return jsonify({"error": {"message": "Campo 'model' é obrigatório", "type": "invalid_request_error", "param": "model"}}), 400
|
||||||
|
|
||||||
|
try:
|
||||||
|
model_config = get_model_config(model_name)
|
||||||
|
except ValueError as e:
|
||||||
|
return jsonify({"error": {"message": str(e), "type": "invalid_request_error", "code": "model_not_found"}}), 404
|
||||||
|
|
||||||
|
# Redireciona para a lógica existente baseado no tipo
|
||||||
|
if model_config.get("type") == "agent":
|
||||||
|
return _handle_agent_completion(model_name, model_config, body)
|
||||||
|
|
||||||
|
# Lógica para modelos
|
||||||
|
prompt = body.get("prompt")
|
||||||
|
if not prompt:
|
||||||
|
return jsonify({"error": {"message": "Campo 'prompt' é obrigatório", "type": "invalid_request_error", "param": "prompt"}}), 400
|
||||||
|
|
||||||
|
params = model_config.get("params", {}).copy()
|
||||||
|
for k in ["temperature", "top_p", "top_k", "max_tokens", "frequency_penalty", "presence_penalty"]:
|
||||||
|
if k in body and body[k] is not None:
|
||||||
|
params[k] = body[k]
|
||||||
|
|
||||||
|
# Converte prompt para formato de mensagem
|
||||||
|
msgs = [{"role": "USER", "content": [{"type": "TEXT", "text": str(prompt)}]}]
|
||||||
|
oci_payload = build_oci_chat_payload(msgs, params)
|
||||||
|
|
||||||
|
model_region = model_config.get("region")
|
||||||
|
compartment_id = model_config.get("compartmentId")
|
||||||
|
model_ocid = model_config.get("id")
|
||||||
|
|
||||||
|
oci_result = oci_chat_invoke(model_region, compartment_id, model_ocid, oci_payload)
|
||||||
|
|
||||||
|
if isinstance(oci_result, dict):
|
||||||
|
output_text = oci_result.get("output_text")
|
||||||
|
usage = oci_result.get("usage", {})
|
||||||
|
else:
|
||||||
|
output_text = None
|
||||||
|
usage = {}
|
||||||
|
|
||||||
|
if output_text is None:
|
||||||
|
output_text = json.dumps(oci_result, ensure_ascii=False)
|
||||||
|
|
||||||
|
if body.get("stream") is True:
|
||||||
|
return Response(
|
||||||
|
stream_with_context(sse_chat_stream(model_name, output_text)),
|
||||||
|
mimetype="text/event-stream"
|
||||||
|
)
|
||||||
|
|
||||||
|
return jsonify(to_openai_text_response(model_name, output_text, usage))
|
||||||
|
|
||||||
|
# ==========================
|
||||||
|
# Endpoints OpenAI v1 — ESTRUTURA /genai/{modelname}/v1/...
|
||||||
|
# ==========================
|
||||||
|
|
||||||
@app.route("/genai/<model_name>/v1/models", methods=["GET"])
|
@app.route("/genai/<model_name>/v1/models", methods=["GET"])
|
||||||
def v1_models(model_name):
|
def v1_models(model_name):
|
||||||
"""
|
"""
|
||||||
@@ -741,8 +1039,8 @@ def v1_chat_completions(model_name):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
return jsonify({"error": f"JSON inválido: {e}"}), 400
|
return jsonify({"error": f"JSON inválido: {e}"}), 400
|
||||||
|
|
||||||
print(f">>> /genai/{model_name}/v1/chat/completions body recebido:")
|
logger.debug(f">>> /genai/{model_name}/v1/chat/completions body recebido:")
|
||||||
print(json.dumps(body, ensure_ascii=False, indent=2))
|
logger.debug(json.dumps(body, ensure_ascii=False, indent=2))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
model_config = get_model_config(model_name)
|
model_config = get_model_config(model_name)
|
||||||
@@ -853,8 +1151,8 @@ def _handle_agent_chat(model_name: str, model_config: Dict[str, Any], body: Dict
|
|||||||
mimetype="text/event-stream"
|
mimetype="text/event-stream"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Resposta normal (agents não retornam token usage real, então usamos None)
|
# Extrair token usage da resposta do agente
|
||||||
usage = {"prompt_tokens": None, "completion_tokens": None, "total_tokens": None}
|
usage = extract_agent_token_usage(agent_resp)
|
||||||
resp = to_openai_chat_response(model_name, response_text, usage)
|
resp = to_openai_chat_response(model_name, response_text, usage)
|
||||||
resp["_agent"] = {"session_id": session_id, "reused": sess.get("reused", False)}
|
resp["_agent"] = {"session_id": session_id, "reused": sess.get("reused", False)}
|
||||||
return jsonify(resp)
|
return jsonify(resp)
|
||||||
@@ -888,7 +1186,7 @@ def _handle_agent_completion(model_name: str, model_config: Dict[str, Any], body
|
|||||||
|
|
||||||
if "error" in sess:
|
if "error" in sess:
|
||||||
# Se falhar ao criar sessão, tenta continuar sem sessão (alguns agents não precisam)
|
# Se falhar ao criar sessão, tenta continuar sem sessão (alguns agents não precisam)
|
||||||
print(f"[warn] Falha ao criar sessão para agent: {sess['error']}")
|
logger.warning(f"Falha ao criar sessão para agent: {sess['error']}")
|
||||||
session_error = True
|
session_error = True
|
||||||
else:
|
else:
|
||||||
session_id = sess["id"]
|
session_id = sess["id"]
|
||||||
@@ -930,8 +1228,8 @@ def _handle_agent_completion(model_name: str, model_config: Dict[str, Any], body
|
|||||||
mimetype="text/event-stream"
|
mimetype="text/event-stream"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Resposta normal (agents não retornam token usage real)
|
# Extrair token usage da resposta do agente
|
||||||
usage = {"prompt_tokens": None, "completion_tokens": None, "total_tokens": None}
|
usage = extract_agent_token_usage(agent_resp)
|
||||||
resp = to_openai_text_response(model_name, response_text, usage)
|
resp = to_openai_text_response(model_name, response_text, usage)
|
||||||
if session_id:
|
if session_id:
|
||||||
resp["_agent"] = {"session_id": session_id, "reused": sess.get("reused", False)}
|
resp["_agent"] = {"session_id": session_id, "reused": sess.get("reused", False)}
|
||||||
@@ -945,8 +1243,8 @@ def v1_text_completions(model_name):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
return jsonify({"error": f"JSON inválido: {e}"}), 400
|
return jsonify({"error": f"JSON inválido: {e}"}), 400
|
||||||
|
|
||||||
print(f">>> /genai/{model_name}/v1/completions body recebido:")
|
logger.debug(f">>> /genai/{model_name}/v1/completions body recebido:")
|
||||||
print(json.dumps(body, ensure_ascii=False, indent=2))
|
logger.debug(json.dumps(body, ensure_ascii=False, indent=2))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
model_config = get_model_config(model_name)
|
model_config = get_model_config(model_name)
|
||||||
@@ -1137,8 +1435,8 @@ def oci_session(model_name):
|
|||||||
return jsonify({"error": f"Modelo '{model_name}' não é um agent. Use type='agent' no JSON."}), 400
|
return jsonify({"error": f"Modelo '{model_name}' não é um agent. Use type='agent' no JSON."}), 400
|
||||||
|
|
||||||
data = request.get_json() or {}
|
data = request.get_json() or {}
|
||||||
print(f">>> /genai/{model_name}/session payload recebido:")
|
logger.debug(f">>> /genai/{model_name}/session payload recebido:")
|
||||||
print(json.dumps(data, ensure_ascii=False, indent=2))
|
logger.debug(json.dumps(data, ensure_ascii=False, indent=2))
|
||||||
|
|
||||||
channel = data.get("channel")
|
channel = data.get("channel")
|
||||||
cuid = data.get("cuid")
|
cuid = data.get("cuid")
|
||||||
@@ -1152,6 +1450,53 @@ def oci_session(model_name):
|
|||||||
response_data = session_controller(model_region, agent_endpoint_id, channel, cuid)
|
response_data = session_controller(model_region, agent_endpoint_id, channel, cuid)
|
||||||
return jsonify(response_data)
|
return jsonify(response_data)
|
||||||
|
|
||||||
|
def _chat_with_retry_on_session_expired(model_region, agent_endpoint_id, session_id, session_key, user_message):
|
||||||
|
"""
|
||||||
|
Envia mensagem ao agente com retry automático em caso de sessão expirada (409).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple: (response_data, session_id, error_response)
|
||||||
|
- Se sucesso: (response_data, session_id, None)
|
||||||
|
- Se erro: (None, None, error_response)
|
||||||
|
"""
|
||||||
|
# Primeira tentativa
|
||||||
|
response_data = ask_agent(model_region, agent_endpoint_id, session_id, user_message)
|
||||||
|
|
||||||
|
# Se retornou erro 409 (sessão inválida), tenta recuperar
|
||||||
|
if isinstance(response_data, dict) and response_data.get("_http_status") == 409:
|
||||||
|
logger.info(f"[chat] Sessão expirou (409), invalidando e recriando...")
|
||||||
|
|
||||||
|
# Invalida sessão local
|
||||||
|
_invalidate_session(session_key)
|
||||||
|
|
||||||
|
# Extrai channel e cuid do session_key
|
||||||
|
channel, cuid = session_key.split(":", 1)
|
||||||
|
|
||||||
|
# Cria nova sessão
|
||||||
|
sess = session_controller(model_region, agent_endpoint_id, channel, cuid)
|
||||||
|
|
||||||
|
if "error" in sess:
|
||||||
|
return None, None, (jsonify({
|
||||||
|
"error": f"Falha ao recriar sessão após erro 409: {sess.get('error')}",
|
||||||
|
"details": sess
|
||||||
|
}), 500)
|
||||||
|
|
||||||
|
new_session_id = sess.get("id")
|
||||||
|
|
||||||
|
# Retry com nova sessão
|
||||||
|
response_data = ask_agent(model_region, agent_endpoint_id, new_session_id, user_message)
|
||||||
|
|
||||||
|
# Se ainda falhou, retorna erro
|
||||||
|
if isinstance(response_data, dict) and response_data.get("_http_status") == 409:
|
||||||
|
return None, None, (jsonify({
|
||||||
|
"error": "Falha persistente de sessão após retry",
|
||||||
|
"details": response_data
|
||||||
|
}), 500)
|
||||||
|
|
||||||
|
return response_data, new_session_id, None
|
||||||
|
|
||||||
|
return response_data, session_id, None
|
||||||
|
|
||||||
@app.route("/genai/<model_name>/chat", methods=["POST"])
|
@app.route("/genai/<model_name>/chat", methods=["POST"])
|
||||||
def oci_chat(model_name):
|
def oci_chat(model_name):
|
||||||
"""
|
"""
|
||||||
@@ -1174,8 +1519,8 @@ def oci_chat(model_name):
|
|||||||
return jsonify({"error": f"Modelo '{model_name}' não é um agent. Use type='agent' no JSON."}), 400
|
return jsonify({"error": f"Modelo '{model_name}' não é um agent. Use type='agent' no JSON."}), 400
|
||||||
|
|
||||||
data = request.get_json() or {}
|
data = request.get_json() or {}
|
||||||
print(f">>> /genai/{model_name}/chat payload recebido:")
|
logger.debug(f">>> /genai/{model_name}/chat payload recebido:")
|
||||||
print(json.dumps(data, ensure_ascii=False, indent=2))
|
logger.debug(json.dumps(data, ensure_ascii=False, indent=2))
|
||||||
|
|
||||||
user_message = data.get("userMessage")
|
user_message = data.get("userMessage")
|
||||||
if not user_message:
|
if not user_message:
|
||||||
@@ -1201,13 +1546,12 @@ def oci_chat(model_name):
|
|||||||
}
|
}
|
||||||
}), 400
|
}), 400
|
||||||
|
|
||||||
# Modo automático: gerencia sessão internamente
|
# Modo automático: gerencia sessão internamente com retry
|
||||||
if channel and cuid:
|
if channel and cuid:
|
||||||
session_key = f"{channel}:{cuid}"
|
session_key = f"{channel}:{cuid}"
|
||||||
|
|
||||||
# Tenta obter/criar sessão
|
# Obter/criar sessão
|
||||||
sess = session_controller(model_region, agent_endpoint_id, channel, cuid)
|
sess = session_controller(model_region, agent_endpoint_id, channel, cuid)
|
||||||
|
|
||||||
if "error" in sess:
|
if "error" in sess:
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"error": f"Falha ao criar sessão: {sess.get('error')}",
|
"error": f"Falha ao criar sessão: {sess.get('error')}",
|
||||||
@@ -1216,45 +1560,24 @@ def oci_chat(model_name):
|
|||||||
|
|
||||||
session_id = sess.get("id")
|
session_id = sess.get("id")
|
||||||
|
|
||||||
# Primeira tentativa
|
# Enviar mensagem com retry automático
|
||||||
response_data = ask_agent(model_region, agent_endpoint_id, session_id, user_message)
|
response_data, session_id, error = _chat_with_retry_on_session_expired(
|
||||||
|
model_region, agent_endpoint_id, session_id, session_key, user_message
|
||||||
|
)
|
||||||
|
|
||||||
# Se retornou erro 409 (sessão inválida), tenta recuperar
|
if error:
|
||||||
if isinstance(response_data, dict) and response_data.get("_http_status") == 409:
|
return error
|
||||||
print(f"[chat] Sessão expirou (409), invalidando e recriando...")
|
|
||||||
|
|
||||||
# Invalida sessão local
|
# Extrair token usage e retornar
|
||||||
_invalidate_session(session_key)
|
usage = extract_agent_token_usage(response_data)
|
||||||
|
|
||||||
# Cria nova sessão
|
|
||||||
sess = session_controller(model_region, agent_endpoint_id, channel, cuid)
|
|
||||||
|
|
||||||
if "error" in sess:
|
|
||||||
return jsonify({
|
|
||||||
"error": f"Falha ao recriar sessão após erro 409: {sess.get('error')}",
|
|
||||||
"details": sess
|
|
||||||
}), 500
|
|
||||||
|
|
||||||
session_id = sess.get("id")
|
|
||||||
|
|
||||||
# Retry com nova sessão
|
|
||||||
response_data = ask_agent(model_region, agent_endpoint_id, session_id, user_message)
|
|
||||||
|
|
||||||
# Se ainda falhou, retorna erro
|
|
||||||
if isinstance(response_data, dict) and response_data.get("_http_status") == 409:
|
|
||||||
return jsonify({
|
|
||||||
"error": "Falha persistente de sessão após retry",
|
|
||||||
"details": response_data
|
|
||||||
}), 500
|
|
||||||
|
|
||||||
# Retorna resposta com informações de sessão
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"agentResponse": response_data,
|
"agentResponse": response_data,
|
||||||
"sessionInfo": {
|
"sessionInfo": {
|
||||||
"sessionId": session_id,
|
"sessionId": session_id,
|
||||||
"sessionKey": session_key,
|
"sessionKey": session_key,
|
||||||
"reused": sess.get("reused", False)
|
"reused": sess.get("reused", False)
|
||||||
}
|
},
|
||||||
|
"usage": usage
|
||||||
})
|
})
|
||||||
|
|
||||||
# Modo manual: usa sessionId fornecido
|
# Modo manual: usa sessionId fornecido
|
||||||
@@ -1269,7 +1592,13 @@ def oci_chat(model_name):
|
|||||||
"details": response_data
|
"details": response_data
|
||||||
}), 409
|
}), 409
|
||||||
|
|
||||||
return jsonify({"agentResponse": response_data})
|
# Extrair token usage da resposta do agente
|
||||||
|
usage = extract_agent_token_usage(response_data)
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"agentResponse": response_data,
|
||||||
|
"usage": usage
|
||||||
|
})
|
||||||
|
|
||||||
@app.route("/genai/<model_name>/inference", methods=["POST"])
|
@app.route("/genai/<model_name>/inference", methods=["POST"])
|
||||||
def oci_inference(model_name):
|
def oci_inference(model_name):
|
||||||
@@ -1287,8 +1616,8 @@ def oci_inference(model_name):
|
|||||||
return jsonify({"error": f"'{model_name}' é um agent. Use /genai/{model_name}/chat ao invés de /inference."}), 400
|
return jsonify({"error": f"'{model_name}' é um agent. Use /genai/{model_name}/chat ao invés de /inference."}), 400
|
||||||
|
|
||||||
data = request.get_json() or {}
|
data = request.get_json() or {}
|
||||||
print(f">>> /genai/{model_name}/inference payload recebido:")
|
logger.debug(f">>> /genai/{model_name}/inference payload recebido:")
|
||||||
print(json.dumps(data, ensure_ascii=False, indent=2))
|
logger.debug(json.dumps(data, ensure_ascii=False, indent=2))
|
||||||
|
|
||||||
prompt = data.get("prompt")
|
prompt = data.get("prompt")
|
||||||
if not prompt:
|
if not prompt:
|
||||||
@@ -1313,13 +1642,7 @@ def oci_inference(model_name):
|
|||||||
})
|
})
|
||||||
|
|
||||||
try:
|
try:
|
||||||
endpoint = f"https://inference.generativeai.{model_region}.oci.oraclecloud.com"
|
client = get_oci_inference_client(model_region)
|
||||||
client = oci.generative_ai_inference.GenerativeAiInferenceClient(
|
|
||||||
config=config,
|
|
||||||
service_endpoint=endpoint,
|
|
||||||
retry_strategy=oci.retry.NoneRetryStrategy(),
|
|
||||||
timeout=(10, 240)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Cria mensagem
|
# Cria mensagem
|
||||||
content = oci.generative_ai_inference.models.TextContent()
|
content = oci.generative_ai_inference.models.TextContent()
|
||||||
@@ -1347,12 +1670,18 @@ def oci_inference(model_name):
|
|||||||
chat_response = client.chat(chat_detail)
|
chat_response = client.chat(chat_detail)
|
||||||
chat_choices = chat_response.data.chat_response.choices
|
chat_choices = chat_response.data.chat_response.choices
|
||||||
|
|
||||||
|
# Extrair token usage da resposta do modelo
|
||||||
|
usage = extract_token_usage(chat_response)
|
||||||
|
|
||||||
chat_data = {
|
chat_data = {
|
||||||
"text": chat_choices[0].message.content[0].text,
|
"text": chat_choices[0].message.content[0].text,
|
||||||
"finish_reason": chat_choices[0].finish_reason
|
"finish_reason": chat_choices[0].finish_reason
|
||||||
}
|
}
|
||||||
|
|
||||||
return jsonify({"response": chat_data})
|
return jsonify({
|
||||||
|
"response": chat_data,
|
||||||
|
"usage": usage
|
||||||
|
})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return jsonify({"error": str(e)}), 500
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
@@ -1361,6 +1690,6 @@ def oci_inference(model_name):
|
|||||||
# ==========================
|
# ==========================
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
print("=" * 60)
|
logger.info("=" * 60)
|
||||||
print("OCI GenAI Proxy v2.0.3")
|
logger.info("OCI GenAI Proxy v2.0.3")
|
||||||
app.run(host='0.0.0.0', port=8000, debug=False)
|
app.run(host='0.0.0.0', port=8000, debug=False)
|
||||||
|
|||||||
Reference in New Issue
Block a user