Update app.py
This commit is contained in:
@@ -3,6 +3,8 @@ import oci
|
|||||||
import requests
|
import requests
|
||||||
import os
|
import os
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
import uuid
|
||||||
|
import time
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
@@ -52,10 +54,6 @@ SESSION_STORE = {}
|
|||||||
SESSION_TTL = timedelta(hours=2)
|
SESSION_TTL = timedelta(hours=2)
|
||||||
|
|
||||||
def session_controller(region, agent_endpoint_id, channel, cuid):
|
def session_controller(region, agent_endpoint_id, channel, cuid):
|
||||||
"""
|
|
||||||
Controla sessões com o agente, reaproveitando se estiver dentro do TTL (2h).
|
|
||||||
A cada interação, a sessão é renovada (sliding TTL).
|
|
||||||
"""
|
|
||||||
session_key = f"{channel}:{cuid}"
|
session_key = f"{channel}:{cuid}"
|
||||||
now = datetime.utcnow()
|
now = datetime.utcnow()
|
||||||
|
|
||||||
@@ -70,7 +68,6 @@ def session_controller(region, agent_endpoint_id, channel, cuid):
|
|||||||
"reused": True
|
"reused": True
|
||||||
}
|
}
|
||||||
|
|
||||||
# Sessão expirada ou inexistente → cria nova
|
|
||||||
if TEST_MODE:
|
if TEST_MODE:
|
||||||
new_session_id = f"test_session_{agent_endpoint_id[:8]}_{int(now.timestamp())}"
|
new_session_id = f"test_session_{agent_endpoint_id[:8]}_{int(now.timestamp())}"
|
||||||
SESSION_STORE[session_key] = {
|
SESSION_STORE[session_key] = {
|
||||||
@@ -114,33 +111,12 @@ def session_controller(region, agent_endpoint_id, channel, cuid):
|
|||||||
return {"error": str(e), "sessionKey": session_key}
|
return {"error": str(e), "sessionKey": session_key}
|
||||||
|
|
||||||
# --------------------------
|
# --------------------------
|
||||||
# Funções de interação
|
# Inferência GenAI
|
||||||
# --------------------------
|
# --------------------------
|
||||||
|
|
||||||
def ask_agent(region, agent_endpoint_id, session_id, user_message):
|
|
||||||
if TEST_MODE:
|
|
||||||
return {
|
|
||||||
"message": f"Resposta simulada para: {user_message}",
|
|
||||||
"sessionId": session_id,
|
|
||||||
"timestamp": datetime.utcnow().isoformat() + "Z"
|
|
||||||
}
|
|
||||||
|
|
||||||
session = requests.Session()
|
|
||||||
session.auth = signer
|
|
||||||
base_url = f"https://agent-runtime.generativeai.{region}.oci.oraclecloud.com/20240531"
|
|
||||||
chat_url = f"{base_url}/agentEndpoints/{agent_endpoint_id}/actions/chat"
|
|
||||||
payload = {
|
|
||||||
"userMessage": user_message,
|
|
||||||
"shouldStream": False,
|
|
||||||
"sessionId": session_id
|
|
||||||
}
|
|
||||||
response = session.post(chat_url, json=payload)
|
|
||||||
response.raise_for_status()
|
|
||||||
return response.json()
|
|
||||||
|
|
||||||
def call_inference_model(region, compartment_id, model_id, prompt):
|
def call_inference_model(region, compartment_id, model_id, prompt):
|
||||||
if TEST_MODE:
|
if TEST_MODE:
|
||||||
return {"response": f"Resposta simulada para o prompt: {prompt}"}
|
return {"response": {"text": f"Resposta simulada: {prompt}", "finish_reason": "stop"}}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
endpoint = f"https://inference.generativeai.{region}.oci.oraclecloud.com"
|
endpoint = f"https://inference.generativeai.{region}.oci.oraclecloud.com"
|
||||||
@@ -151,71 +127,70 @@ def call_inference_model(region, compartment_id, model_id, prompt):
|
|||||||
retry_strategy=oci.retry.NoneRetryStrategy(),
|
retry_strategy=oci.retry.NoneRetryStrategy(),
|
||||||
timeout=(10, 240)
|
timeout=(10, 240)
|
||||||
)
|
)
|
||||||
chat_detail = oci.generative_ai_inference.models.ChatDetails()
|
|
||||||
|
|
||||||
content = oci.generative_ai_inference.models.TextContent()
|
content = oci.generative_ai_inference.models.TextContent(text=prompt)
|
||||||
content.text = f"{prompt}"
|
message = oci.generative_ai_inference.models.Message(role="USER", content=[content])
|
||||||
message = oci.generative_ai_inference.models.Message()
|
|
||||||
message.role = "USER"
|
|
||||||
message.content = [content]
|
|
||||||
|
|
||||||
chat_request = oci.generative_ai_inference.models.GenericChatRequest()
|
chat_request = oci.generative_ai_inference.models.GenericChatRequest(
|
||||||
chat_request.api_format = oci.generative_ai_inference.models.BaseChatRequest.API_FORMAT_GENERIC
|
api_format=oci.generative_ai_inference.models.BaseChatRequest.API_FORMAT_GENERIC,
|
||||||
chat_request.messages = [message]
|
messages=[message],
|
||||||
chat_request.max_tokens = 50000
|
max_tokens=50000,
|
||||||
chat_request.temperature = 1
|
temperature=1,
|
||||||
chat_request.top_p = 1
|
top_p=1,
|
||||||
chat_request.top_k = 0
|
top_k=0
|
||||||
|
)
|
||||||
|
|
||||||
chat_detail.serving_mode = oci.generative_ai_inference.models.OnDemandServingMode(model_id=model_id)
|
chat_detail = oci.generative_ai_inference.models.ChatDetails(
|
||||||
chat_detail.chat_request = chat_request
|
serving_mode=oci.generative_ai_inference.models.OnDemandServingMode(model_id=model_id),
|
||||||
chat_detail.compartment_id = compartment_id
|
chat_request=chat_request,
|
||||||
|
compartment_id=compartment_id
|
||||||
|
)
|
||||||
|
|
||||||
chat_response = generative_ai_inference_client.chat(chat_detail)
|
chat_response = generative_ai_inference_client.chat(chat_detail)
|
||||||
chat_choices = chat_response.data.chat_response.choices
|
choice = chat_response.data.chat_response.choices[0]
|
||||||
chat_data = {
|
|
||||||
"text": chat_choices[0].message.content[0].text,
|
return {"response": {
|
||||||
"finish_reason": chat_choices[0].finish_reason
|
"text": choice.message.content[0].text,
|
||||||
}
|
"finish_reason": choice.finish_reason
|
||||||
|
}}
|
||||||
|
|
||||||
return {"response": chat_data}
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {"error": str(e)}
|
return {"error": str(e)}
|
||||||
|
|
||||||
# --------------------------
|
# --------------------------
|
||||||
# Segurança
|
# Autenticação
|
||||||
# --------------------------
|
# --------------------------
|
||||||
|
|
||||||
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.")
|
print("AVISO: API_KEY não configurada.")
|
||||||
return
|
return
|
||||||
provided_key = request.headers.get("X-API-Key")
|
|
||||||
if provided_key != expected_key:
|
auth_header = request.headers.get("Authorization", "")
|
||||||
abort(401, description="Chave de API inválida ou ausente.")
|
token = ""
|
||||||
|
if auth_header.startswith("Bearer "):
|
||||||
|
token = auth_header.split("Bearer ")[1].strip()
|
||||||
|
else:
|
||||||
|
token = request.headers.get("X-API-Key")
|
||||||
|
|
||||||
|
if token != expected_key:
|
||||||
|
abort(401, description="API key inválida.")
|
||||||
|
|
||||||
@app.before_request
|
@app.before_request
|
||||||
def before_all_requests():
|
def before_all_requests():
|
||||||
check_api_key()
|
check_api_key()
|
||||||
|
|
||||||
# --------------------------
|
# --------------------------
|
||||||
# Endpoints
|
# Endpoints REST
|
||||||
# --------------------------
|
# --------------------------
|
||||||
|
|
||||||
@app.route("/", methods=["GET"])
|
@app.route("/", methods=["GET"])
|
||||||
def test():
|
def test():
|
||||||
return jsonify({"test": "ok"})
|
return jsonify({"status": "ok"})
|
||||||
|
|
||||||
@app.route("/test/<myvar>/copy", methods=["GET"])
|
|
||||||
def var_copy(myvar):
|
|
||||||
return jsonify({"myvar": myvar})
|
|
||||||
|
|
||||||
@app.route("/genai-agent/<region>/<agent_endpoint_id>/session", methods=["POST"])
|
@app.route("/genai-agent/<region>/<agent_endpoint_id>/session", methods=["POST"])
|
||||||
def manage_session(region, agent_endpoint_id):
|
def manage_session(region, agent_endpoint_id):
|
||||||
"""
|
|
||||||
Reaproveita ou cria uma sessão nova com base em channel + cuid.
|
|
||||||
"""
|
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
channel = data.get("channel")
|
channel = data.get("channel")
|
||||||
cuid = data.get("cuid")
|
cuid = data.get("cuid")
|
||||||
@@ -230,8 +205,23 @@ def agent_chat(region, agent_endpoint_id, session_id):
|
|||||||
user_message = data.get("userMessage")
|
user_message = data.get("userMessage")
|
||||||
if not user_message:
|
if not user_message:
|
||||||
return jsonify({"error": "userMessage é obrigatório"}), 400
|
return jsonify({"error": "userMessage é obrigatório"}), 400
|
||||||
response_data = ask_agent(region, agent_endpoint_id, session_id, user_message)
|
|
||||||
return jsonify({"agentResponse": response_data})
|
try:
|
||||||
|
base_url = f"https://agent-runtime.generativeai.{region}.oci.oraclecloud.com/20240531"
|
||||||
|
chat_url = f"{base_url}/agentEndpoints/{agent_endpoint_id}/actions/chat"
|
||||||
|
session_obj = requests.Session()
|
||||||
|
session_obj.auth = signer
|
||||||
|
payload = {
|
||||||
|
"userMessage": user_message,
|
||||||
|
"shouldStream": False,
|
||||||
|
"sessionId": session_id
|
||||||
|
}
|
||||||
|
response = session_obj.post(chat_url, json=payload)
|
||||||
|
response.raise_for_status()
|
||||||
|
return jsonify({"agentResponse": response.json()})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"error": str(e)}), 500
|
||||||
|
|
||||||
@app.route("/genai/<region>/<compartment_id>/<model_id>/inference", methods=["POST"])
|
@app.route("/genai/<region>/<compartment_id>/<model_id>/inference", methods=["POST"])
|
||||||
def inference(region, compartment_id, model_id):
|
def inference(region, compartment_id, model_id):
|
||||||
@@ -239,11 +229,90 @@ def inference(region, compartment_id, model_id):
|
|||||||
prompt = data.get("prompt")
|
prompt = data.get("prompt")
|
||||||
if not prompt:
|
if not prompt:
|
||||||
return jsonify({"error": "Campo 'prompt' é obrigatório."}), 400
|
return jsonify({"error": "Campo 'prompt' é obrigatório."}), 400
|
||||||
|
|
||||||
response_data = call_inference_model(region, compartment_id, model_id, prompt)
|
response_data = call_inference_model(region, compartment_id, model_id, prompt)
|
||||||
return jsonify(response_data)
|
return jsonify(response_data)
|
||||||
|
|
||||||
|
@app.route("/genai/<region>/<compartment_id>/<model_id>/v1/chat/completions", methods=["POST"])
|
||||||
|
def openai_compatible_chat(region, compartment_id, model_id):
|
||||||
|
data = request.get_json()
|
||||||
|
messages = data.get("messages", [])
|
||||||
|
temperature = data.get("temperature", 1)
|
||||||
|
top_p = data.get("top_p", 1)
|
||||||
|
top_k = data.get("top_k", 0)
|
||||||
|
max_tokens = data.get("max_tokens", 1000)
|
||||||
|
|
||||||
|
user_prompt = next((m["content"] for m in reversed(messages) if m["role"] == "user"), None)
|
||||||
|
if not user_prompt:
|
||||||
|
return jsonify({"error": "mensagem do usuário é obrigatória"}), 400
|
||||||
|
|
||||||
|
response = call_inference_model(region, compartment_id, model_id, user_prompt)
|
||||||
|
if "error" in response:
|
||||||
|
return jsonify({"error": response["error"]}), 500
|
||||||
|
|
||||||
|
result_text = response["response"]["text"]
|
||||||
|
finish_reason = response["response"].get("finish_reason", "stop")
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"id": f"chatcmpl-{uuid.uuid4().hex[:12]}",
|
||||||
|
"object": "chat.completion",
|
||||||
|
"created": int(time.time()),
|
||||||
|
"model": model_id,
|
||||||
|
"choices": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"message": {"role": "assistant", "content": result_text},
|
||||||
|
"finish_reason": finish_reason
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
@app.route("/genai/<region>/<compartment_id>/<model_id>/v1/completions", methods=["POST"])
|
||||||
|
def openai_compatible_completion(region, compartment_id, model_id):
|
||||||
|
data = request.get_json()
|
||||||
|
prompt = data.get("prompt")
|
||||||
|
temperature = data.get("temperature", 1)
|
||||||
|
top_p = data.get("top_p", 1)
|
||||||
|
top_k = data.get("top_k", 0)
|
||||||
|
max_tokens = data.get("max_tokens", 1000)
|
||||||
|
stop = data.get("stop")
|
||||||
|
|
||||||
|
if not prompt:
|
||||||
|
return jsonify({"error": "Campo 'prompt' é obrigatório."}), 400
|
||||||
|
|
||||||
|
response = call_inference_model(region, compartment_id, model_id, prompt)
|
||||||
|
if "error" in response:
|
||||||
|
return jsonify({"error": response["error"]}), 500
|
||||||
|
|
||||||
|
result_text = response["response"]["text"]
|
||||||
|
finish_reason = response["response"].get("finish_reason", "stop")
|
||||||
|
|
||||||
|
if stop:
|
||||||
|
if isinstance(stop, list):
|
||||||
|
for s in stop:
|
||||||
|
if s in result_text:
|
||||||
|
result_text = result_text.split(s)[0]
|
||||||
|
break
|
||||||
|
elif isinstance(stop, str) and stop in result_text:
|
||||||
|
result_text = result_text.split(stop)[0]
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"id": f"cmpl-{uuid.uuid4().hex[:12]}",
|
||||||
|
"object": "text_completion",
|
||||||
|
"created": int(time.time()),
|
||||||
|
"model": model_id,
|
||||||
|
"choices": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"text": result_text,
|
||||||
|
"logprobs": None,
|
||||||
|
"finish_reason": finish_reason
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
# --------------------------
|
# --------------------------
|
||||||
# Main
|
# Inicialização
|
||||||
# --------------------------
|
# --------------------------
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
Reference in New Issue
Block a user