mirror of
https://github.com/hoshikawa2/first_contas.git
synced 2026-07-10 02:34:20 +00:00
first commit
This commit is contained in:
304
legacy_reference/workflows/actions/common/actions.py
Normal file
304
legacy_reference/workflows/actions/common/actions.py
Normal file
@@ -0,0 +1,304 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import logging
|
||||
|
||||
from typing import Any
|
||||
from agente_contas_tim.constants.ic_tags_enum import SADTag, TMDTag
|
||||
from agente_contas_tim.integrations.rct_policy import RCTOperation, rct_tags_for_attempt
|
||||
from agente_contas_tim.workflows.actions.registry import (
|
||||
WorkflowRuntimeContext,
|
||||
workflow_action,
|
||||
)
|
||||
from agente_contas_tim.workflows.runtime_types import ActionResult
|
||||
from agente_contas_tim.workflows.actions.common.helpers import (
|
||||
_emit_ic,
|
||||
_normalize_bool,
|
||||
_result_failed_or_missing_data,
|
||||
_runtime_llm_callbacks,
|
||||
_runtime_llm_metadata,
|
||||
_to_dict,
|
||||
)
|
||||
|
||||
logger = logging.getLogger("agente_contas_tim.workflows.actions.tim_actions")
|
||||
|
||||
|
||||
@workflow_action("finalizar_atendimento_action")
|
||||
def finalizar_atendimento_action(
|
||||
state: dict[str, Any],
|
||||
params: dict[str, Any],
|
||||
runtime: WorkflowRuntimeContext,
|
||||
) -> ActionResult:
|
||||
input_state = state.get("input", {}) if isinstance(state, dict) else {}
|
||||
if not isinstance(input_state, dict):
|
||||
input_state = {}
|
||||
_emit_ic(SADTag.FINALIZACAO, input_state)
|
||||
status = str(params.get("status", "")).strip()
|
||||
summary = str(params.get("summary", "")).strip()
|
||||
return ActionResult.ok(
|
||||
{
|
||||
"success": True,
|
||||
"status": status,
|
||||
"summary": summary,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@workflow_action("formatar_capability_resposta")
|
||||
def formatar_capability_resposta(
|
||||
state: dict[str, Any],
|
||||
params: dict[str, Any],
|
||||
runtime: WorkflowRuntimeContext,
|
||||
) -> ActionResult:
|
||||
vars_state = state.get("vars", {}) if isinstance(state, dict) else {}
|
||||
source_node = str(params.get("source_node", "resolve"))
|
||||
payload = vars_state.get(source_node, {})
|
||||
if not isinstance(payload, dict):
|
||||
payload = {}
|
||||
|
||||
input_state = state.get("input", {}) if isinstance(state, dict) else {}
|
||||
msisdn = str(params.get("msisdn", input_state.get("msisdn", "")))
|
||||
servico = str(params.get("servico", input_state.get("servico", "")))
|
||||
nome_plano = str(params.get("nome_plano", input_state.get("nome_plano", "")))
|
||||
tipo = str(params.get("tipo", "")).strip().lower()
|
||||
|
||||
instrucoes = str(payload.get("content", "")).strip()
|
||||
mensagem = ""
|
||||
|
||||
if tipo == "vas_estrategico":
|
||||
mensagem = (
|
||||
f"O serviço {servico} já está incluso no seu plano "
|
||||
f"na linha de final {msisdn[-2:]} e não gera "
|
||||
"cobrança adicional na fatura."
|
||||
)
|
||||
elif tipo == "valor_divergente":
|
||||
mensagem = (
|
||||
f"Identificamos uma alteração no valor do plano "
|
||||
f"na linha de final {msisdn[-2:]}. Vou verificar "
|
||||
"os detalhes para você."
|
||||
)
|
||||
elif tipo == "pro_rata":
|
||||
mensagem = (
|
||||
f"A fatura da linha de final {msisdn[-2:]} possui uma "
|
||||
"cobrança proporcional (pro rata) porque houve mudança "
|
||||
"de plano durante o ciclo de faturamento. Na próxima "
|
||||
"fatura o valor volta ao normal do novo plano."
|
||||
)
|
||||
elif tipo == "termino_desconto":
|
||||
_emit_ic(TMDTag.CHEGADA_FLUXO, input_state)
|
||||
plan_info = f" do plano {nome_plano}" if nome_plano else ""
|
||||
mensagem = (
|
||||
f"O desconto de fidelidade{plan_info} vinculado à "
|
||||
f"linha de final {msisdn[-2:]} chegou ao fim. "
|
||||
"O período promocional contratado expirou e o valor "
|
||||
"do plano volta ao preço original."
|
||||
)
|
||||
_emit_ic(TMDTag.FIM_FLUXO, input_state)
|
||||
|
||||
response: dict[str, Any] = {
|
||||
"success": True,
|
||||
"instrucoes": instrucoes,
|
||||
"mensagem": mensagem,
|
||||
"msisdn": msisdn,
|
||||
}
|
||||
if servico:
|
||||
response["servico"] = servico
|
||||
if nome_plano:
|
||||
response["nome_plano"] = nome_plano
|
||||
return ActionResult.ok(response)
|
||||
|
||||
|
||||
@workflow_action("combinar_divergencia")
|
||||
def combinar_divergencia(
|
||||
state: dict[str, Any],
|
||||
params: dict[str, Any],
|
||||
runtime: WorkflowRuntimeContext,
|
||||
) -> ActionResult:
|
||||
vars_state = state.get("vars", {}) if isinstance(state, dict) else {}
|
||||
divergence = vars_state.get("consultar_divergencia", {})
|
||||
explanation = vars_state.get("invoice_explanation", {})
|
||||
|
||||
if not isinstance(divergence, dict):
|
||||
divergence = {}
|
||||
if not isinstance(explanation, dict):
|
||||
explanation = {}
|
||||
|
||||
input_state = state.get("input", {}) if isinstance(state, dict) else {}
|
||||
msisdn = str(input_state.get("msisdn", ""))
|
||||
|
||||
resumo = str(
|
||||
divergence.get("resumo")
|
||||
or divergence.get("mensagem")
|
||||
or ""
|
||||
).strip()
|
||||
detalhes = divergence.get("detalhes", {})
|
||||
explicacao = str(explanation.get("mensagem", "")).strip()
|
||||
|
||||
success = bool(resumo or explicacao)
|
||||
mensagem = resumo or explicacao
|
||||
|
||||
return ActionResult.ok(
|
||||
{
|
||||
"success": success,
|
||||
"resumo": resumo,
|
||||
"detalhes": detalhes if isinstance(detalhes, dict) else {},
|
||||
"mensagem": mensagem,
|
||||
"explicacao": explicacao,
|
||||
"msisdn": msisdn,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@workflow_action("consultar_divergencia")
|
||||
def query_divergence(
|
||||
state: dict[str, Any],
|
||||
params: dict[str, Any],
|
||||
runtime: WorkflowRuntimeContext,
|
||||
) -> ActionResult:
|
||||
"""Consulta resumo de divergência da fatura."""
|
||||
result = runtime.factory.create_divergence(
|
||||
msisdn=str(params["msisdn"]),
|
||||
).execute()
|
||||
if _result_failed_or_missing_data(result, state=state):
|
||||
return ActionResult.fail(
|
||||
result.error or "Falha na consulta de divergencia",
|
||||
**result.metadata,
|
||||
)
|
||||
return ActionResult.ok(_to_dict(result.data), **result.metadata)
|
||||
|
||||
|
||||
@workflow_action("resolve_capability")
|
||||
def resolve_capability_action(
|
||||
state: dict[str, Any],
|
||||
params: dict[str, Any],
|
||||
runtime: WorkflowRuntimeContext,
|
||||
) -> ActionResult:
|
||||
"""Resolve uma capability via LLM Gateway (sem invocar LLM)."""
|
||||
if runtime.llm_gateway is None:
|
||||
return ActionResult.ok({"content": "", "source": "unavailable"})
|
||||
|
||||
capability_id = str(params.get("capability_id", "")).strip()
|
||||
if not capability_id:
|
||||
return ActionResult.fail(
|
||||
"capability_id obrigatorio para resolve_capability",
|
||||
)
|
||||
|
||||
try:
|
||||
resolved = runtime.llm_gateway.resolve_capability(
|
||||
capability_id=capability_id,
|
||||
)
|
||||
return ActionResult.ok({
|
||||
"capability_id": resolved.capability_id,
|
||||
"prompt_id": resolved.prompt_id,
|
||||
"content": resolved.content,
|
||||
"source": resolved.source,
|
||||
"version": resolved.version,
|
||||
})
|
||||
except Exception as exc:
|
||||
return ActionResult.ok(
|
||||
{"content": "", "source": "fallback", "error": str(exc)},
|
||||
)
|
||||
|
||||
|
||||
@workflow_action("no_op")
|
||||
def no_op(
|
||||
state: dict[str, Any],
|
||||
params: dict[str, Any],
|
||||
runtime: WorkflowRuntimeContext,
|
||||
) -> ActionResult:
|
||||
return ActionResult.ok({})
|
||||
|
||||
|
||||
@workflow_action("llm_capability")
|
||||
def llm_capability(
|
||||
state: dict[str, Any],
|
||||
params: dict[str, Any],
|
||||
runtime: WorkflowRuntimeContext,
|
||||
) -> ActionResult:
|
||||
if runtime.llm_gateway is None:
|
||||
return ActionResult.fail("LLM gateway nao configurado")
|
||||
|
||||
capability_id = str(params.get("capability_id", "")).strip()
|
||||
if not capability_id:
|
||||
return ActionResult.fail("capability_id obrigatorio para llm_capability")
|
||||
|
||||
variables = params.get("variables", {})
|
||||
if not isinstance(variables, dict):
|
||||
return ActionResult.fail("variables deve ser um objeto")
|
||||
|
||||
user_text_raw = params.get("user_text")
|
||||
user_text = None if user_text_raw is None else str(user_text_raw)
|
||||
|
||||
result = runtime.llm_gateway.execute(
|
||||
capability_id=capability_id,
|
||||
variables=variables,
|
||||
user_text=user_text,
|
||||
callbacks=_runtime_llm_callbacks(runtime),
|
||||
tags=["workflow_action"],
|
||||
metadata=_runtime_llm_metadata(runtime),
|
||||
)
|
||||
return ActionResult.ok(
|
||||
{
|
||||
"capability_id": result.capability_id,
|
||||
"prompt_id": result.prompt_id,
|
||||
"content": result.content,
|
||||
"source": result.source,
|
||||
"version": result.version,
|
||||
"metadata": dict(result.metadata),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@workflow_action("buscar_fatura")
|
||||
def buscar_fatura(
|
||||
state: dict[str, Any],
|
||||
params: dict[str, Any],
|
||||
runtime: WorkflowRuntimeContext,
|
||||
) -> ActionResult:
|
||||
"""Busca fatura (PDF) do cliente ou dados interpretados."""
|
||||
input_state = state.get("input", {}) if isinstance(state, dict) else {}
|
||||
if not isinstance(input_state, dict):
|
||||
input_state = {}
|
||||
tentativa = int(params.get("tentativa_anterior") or 0) + 1
|
||||
|
||||
result = runtime.factory.create_bill_pdf(
|
||||
invoice_id=str(params["invoice_id"]),
|
||||
msisdn=str(params["msisdn"]),
|
||||
customer_id=str(params["customer_id"]),
|
||||
output=str(params.get("output", "")),
|
||||
include_danfe=_normalize_bool(params.get("include_danfe", False)),
|
||||
).execute()
|
||||
|
||||
if _result_failed_or_missing_data(result, state=state):
|
||||
for tag in rct_tags_for_attempt(RCTOperation.PDF_FATURA, tentativa, success=False):
|
||||
_emit_ic(tag, input_state)
|
||||
return ActionResult.fail(
|
||||
result.error or "Falha ao buscar fatura",
|
||||
**result.metadata,
|
||||
)
|
||||
|
||||
for tag in rct_tags_for_attempt(RCTOperation.PDF_FATURA, tentativa, success=True):
|
||||
_emit_ic(tag, input_state)
|
||||
|
||||
payload = _to_dict(result.data)
|
||||
if isinstance(payload, dict):
|
||||
file_content = payload.get("file_content")
|
||||
if isinstance(file_content, (bytes, bytearray)):
|
||||
payload["file_content_b64"] = base64.b64encode(
|
||||
bytes(file_content)
|
||||
).decode("ascii")
|
||||
payload["file_content"] = None
|
||||
|
||||
return ActionResult.ok(payload, **result.metadata)
|
||||
|
||||
|
||||
__all__ = [
|
||||
'finalizar_atendimento_action',
|
||||
'formatar_capability_resposta',
|
||||
'combinar_divergencia',
|
||||
'query_divergence',
|
||||
'resolve_capability_action',
|
||||
'no_op',
|
||||
'llm_capability',
|
||||
'buscar_fatura',
|
||||
]
|
||||
Reference in New Issue
Block a user