biatoolkit 1.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- biatoolkit/__init__.py +4 -0
- biatoolkit/basic_client.py +141 -0
- biatoolkit/sankhya_call.py +433 -0
- biatoolkit/schema/__init__.py +1 -0
- biatoolkit/schema/header.py +19 -0
- biatoolkit/settings.py +106 -0
- biatoolkit/test_sankhya.py +39 -0
- biatoolkit/util.py +228 -0
- biatoolkit-1.2.0.dist-info/METADATA +492 -0
- biatoolkit-1.2.0.dist-info/RECORD +12 -0
- biatoolkit-1.2.0.dist-info/WHEEL +5 -0
- biatoolkit-1.2.0.dist-info/top_level.txt +1 -0
biatoolkit/settings.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""
|
|
2
|
+
biatoolkit.settings
|
|
3
|
+
|
|
4
|
+
Este módulo centraliza todas as configurações globais da Bia Toolkit.
|
|
5
|
+
|
|
6
|
+
Objetivos:
|
|
7
|
+
- Evitar valores hardcoded espalhados pelo código.
|
|
8
|
+
- Permitir override simples via variáveis de ambiente.
|
|
9
|
+
- Facilitar testes, manutenção e futuras extensões.
|
|
10
|
+
|
|
11
|
+
Exemplos de override via environment:
|
|
12
|
+
BIATOOLKIT_HEADER_PREFIX=x-amzn-bedrock-agentcore-runtime-custom
|
|
13
|
+
BIATOOLKIT_AWS_REGION=us-east-1
|
|
14
|
+
BIATOOLKIT_CLIENT_TIMEOUT_SECONDS=60
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
import os
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(frozen=True)
|
|
22
|
+
class BiaToolkitSettings:
|
|
23
|
+
"""
|
|
24
|
+
Objeto imutável (frozen) que representa as configurações da biblioteca.
|
|
25
|
+
|
|
26
|
+
Por que usar dataclass + frozen?
|
|
27
|
+
- Facilita leitura e manutenção.
|
|
28
|
+
- Garante que as configurações não sejam alteradas em runtime,
|
|
29
|
+
evitando efeitos colaterais difíceis de rastrear.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
# ------------------------------------------------------------------
|
|
33
|
+
# Server / Headers
|
|
34
|
+
# ------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
# Prefixo base dos headers customizados aceitos pelo runtime do AgentCore.
|
|
37
|
+
# Exemplo final:
|
|
38
|
+
# x-amzn-bedrock-agentcore-runtime-custom-user-email
|
|
39
|
+
header_prefix: str = "x-amzn-bedrock-agentcore-runtime-custom"
|
|
40
|
+
|
|
41
|
+
# ------------------------------------------------------------------
|
|
42
|
+
# AWS
|
|
43
|
+
# ------------------------------------------------------------------
|
|
44
|
+
|
|
45
|
+
# Região AWS usada para acessar serviços como SSM Parameter Store.
|
|
46
|
+
# Default alinhado com o ambiente do Bia Agent Builder.
|
|
47
|
+
aws_region: str = "sa-east-1"
|
|
48
|
+
|
|
49
|
+
# ------------------------------------------------------------------
|
|
50
|
+
# Client (MCP HTTP Client)
|
|
51
|
+
# ------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
# Timeout padrão (em segundos) para chamadas HTTP ao servidor MCP.
|
|
54
|
+
# Evita requests presos indefinidamente em ambientes instáveis.
|
|
55
|
+
client_timeout_seconds: int = 120
|
|
56
|
+
|
|
57
|
+
# ------------------------------------------------------------------
|
|
58
|
+
# Factory methods
|
|
59
|
+
# ------------------------------------------------------------------
|
|
60
|
+
|
|
61
|
+
@staticmethod
|
|
62
|
+
def from_env() -> "BiaToolkitSettings":
|
|
63
|
+
"""
|
|
64
|
+
Cria uma instância de BiaToolkitSettings a partir de variáveis de ambiente.
|
|
65
|
+
|
|
66
|
+
Comportamento:
|
|
67
|
+
- Cada configuração pode ser sobrescrita individualmente via env.
|
|
68
|
+
- Caso a variável não exista ou seja inválida, usa o valor default.
|
|
69
|
+
|
|
70
|
+
Variáveis suportadas:
|
|
71
|
+
- BIATOOLKIT_HEADER_PREFIX
|
|
72
|
+
- BIATOOLKIT_AWS_REGION
|
|
73
|
+
- BIATOOLKIT_CLIENT_TIMEOUT_SECONDS
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
BiaToolkitSettings: instância configurada.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
# Header prefix (string simples)
|
|
80
|
+
header_prefix = os.getenv(
|
|
81
|
+
"BIATOOLKIT_HEADER_PREFIX",
|
|
82
|
+
"x-amzn-bedrock-agentcore-runtime-custom",
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Região AWS
|
|
86
|
+
aws_region = os.getenv(
|
|
87
|
+
"BIATOOLKIT_AWS_REGION",
|
|
88
|
+
"sa-east-1",
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Timeout do client (conversão segura para int)
|
|
92
|
+
timeout_str = os.getenv(
|
|
93
|
+
"BIATOOLKIT_CLIENT_TIMEOUT_SECONDS",
|
|
94
|
+
"120",
|
|
95
|
+
)
|
|
96
|
+
try:
|
|
97
|
+
timeout = int(timeout_str)
|
|
98
|
+
except ValueError:
|
|
99
|
+
# Se o valor não for um inteiro válido, cai no default
|
|
100
|
+
timeout = 120
|
|
101
|
+
|
|
102
|
+
return BiaToolkitSettings(
|
|
103
|
+
header_prefix=header_prefix,
|
|
104
|
+
aws_region=aws_region,
|
|
105
|
+
client_timeout_seconds=timeout,
|
|
106
|
+
)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import os
|
|
3
|
+
#sys.path.insert(0, os.path.abspath(os.path.dirname(__file__) + '/..'))
|
|
4
|
+
from biatoolkit.sankhya_call import Sankhya, SankhyaHTTPError
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
# Opcional: não depende mais disso, mas pode deixar.
|
|
8
|
+
os.environ.setdefault("SANKHYA_SERVICE_PATH", "/mge/service.sbr")
|
|
9
|
+
|
|
10
|
+
# ⚠️ COLE AQUI seu JSESSIONID em UMA LINHA (sem ENTER no meio)
|
|
11
|
+
REAL = "CrK0qgoMYcnfZsURMi7iarpFKWSVqS-qO3HWMiu8.sankhya-w-78598f67f-n7mcp"
|
|
12
|
+
|
|
13
|
+
if not REAL or REAL == "COLE_SEU_JSESSIONID_AQUI":
|
|
14
|
+
raise SystemExit("Cole seu JSESSIONID na variável REAL (uma linha só) e rode: python -m biatoolkit.test_sankhya")
|
|
15
|
+
|
|
16
|
+
# Base URL do ambiente correto (o novo)
|
|
17
|
+
BASE_URL = "https://ecossistema2.sankhyacloud.com.br"
|
|
18
|
+
|
|
19
|
+
sk = Sankhya()
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
out = sk.load_view(
|
|
23
|
+
"BIA_VW_MB_RULES",
|
|
24
|
+
"1=1",
|
|
25
|
+
fields="COMPLDESC_A,COMPLDESC_B,CONFIDENCE_A_B,LIFT_A_B,CNT_AB,DISPONIVEL_B,DTGERACAO",
|
|
26
|
+
jsessionid=REAL,
|
|
27
|
+
base_url=BASE_URL, # <-- NOVO: injeta a base url aqui
|
|
28
|
+
# url="/mge/service.sbr", # opcional: se quiser passar só o path
|
|
29
|
+
# url="https://ecossistema2.sankhyacloud.com.br/mge/service.sbr", # opcional: URL completa
|
|
30
|
+
)
|
|
31
|
+
print("OK! Resposta (top-level keys):", list(out.keys()))
|
|
32
|
+
print(out)
|
|
33
|
+
if isinstance(out, dict):
|
|
34
|
+
print(json.dumps(out, indent=2, ensure_ascii=False))
|
|
35
|
+
|
|
36
|
+
except SankhyaHTTPError as e:
|
|
37
|
+
print("HTTP ERROR:", e.status_code)
|
|
38
|
+
print("BODY (primeiros 500 chars):")
|
|
39
|
+
print((e.response_text or "")[:500])
|
biatoolkit/util.py
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"""
|
|
2
|
+
biatoolkit.util
|
|
3
|
+
|
|
4
|
+
Este módulo contém a classe BiaUtil, uma "fachada" (facade) com utilidades comuns
|
|
5
|
+
para MCP Servers no ecossistema Bia Agent Builder.
|
|
6
|
+
|
|
7
|
+
Responsabilidades principais:
|
|
8
|
+
- Ler e interpretar headers padronizados enviados no runtime (AWS Bedrock AgentCore).
|
|
9
|
+
- Buscar parâmetros de configuração/segredos primeiro no ambiente (.env/variáveis),
|
|
10
|
+
e como fallback no AWS SSM Parameter Store (cofre de segredos no ambiente produtivo).
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from typing import Optional
|
|
14
|
+
import os
|
|
15
|
+
|
|
16
|
+
import boto3
|
|
17
|
+
from mcp.server.fastmcp import FastMCP
|
|
18
|
+
|
|
19
|
+
from .settings import BiaToolkitSettings
|
|
20
|
+
from .schema.header import Header
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class BiaUtil:
|
|
24
|
+
"""
|
|
25
|
+
Classe utilitária para uso dentro de um MCP Server.
|
|
26
|
+
|
|
27
|
+
Exemplos de uso:
|
|
28
|
+
util = BiaUtil(mcp)
|
|
29
|
+
header = util.get_header()
|
|
30
|
+
token = util.get_parameter("MEU_TOKEN")
|
|
31
|
+
|
|
32
|
+
Observações:
|
|
33
|
+
- Esta classe depende do contexto de requisição do FastMCP (mcp.get_context()).
|
|
34
|
+
- Em produção (AgentCore), apenas alguns headers são repassados pelo runtime.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
# Prefixo base dos headers customizados aceitos no runtime do AgentCore.
|
|
38
|
+
# Exemplo de header final: "x-amzn-bedrock-agentcore-runtime-custom-user-email"
|
|
39
|
+
HEADER_PREFIX = "x-amzn-bedrock-agentcore-runtime-custom"
|
|
40
|
+
|
|
41
|
+
def __init__(self, mcp: FastMCP, settings: Optional[BiaToolkitSettings] = None):
|
|
42
|
+
"""
|
|
43
|
+
Inicializa o utilitário.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
mcp: Instância do FastMCP, usada para acessar o contexto da requisição.
|
|
47
|
+
settings: Configurações opcionais (region, timeout, header_prefix).
|
|
48
|
+
Se None, carrega defaults e overrides via variáveis de ambiente.
|
|
49
|
+
"""
|
|
50
|
+
self.mcp = mcp
|
|
51
|
+
|
|
52
|
+
# Settings podem vir explicitamente (testes/uso avançado) ou via environment.
|
|
53
|
+
self.settings = settings or BiaToolkitSettings.from_env()
|
|
54
|
+
|
|
55
|
+
# Permite sobrescrever o prefixo dos headers via settings.
|
|
56
|
+
# Mantém compatibilidade: self.HEADER_PREFIX é usado na composição das chaves.
|
|
57
|
+
self.HEADER_PREFIX = self.settings.header_prefix
|
|
58
|
+
|
|
59
|
+
def _headers(self) -> dict:
|
|
60
|
+
"""
|
|
61
|
+
Obtém o dicionário de headers da requisição atual via contexto do MCP.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
dict: Headers presentes na requisição (ou {} se ausentes).
|
|
65
|
+
"""
|
|
66
|
+
ctx = self.mcp.get_context()
|
|
67
|
+
# Estrutura esperada no FastMCP: ctx.request_context.request.headers
|
|
68
|
+
return ctx.request_context.request.headers or {}
|
|
69
|
+
|
|
70
|
+
def _h(self, suffix: str):
|
|
71
|
+
"""
|
|
72
|
+
Lê um header customizado do runtime, dado o sufixo.
|
|
73
|
+
|
|
74
|
+
Exemplo:
|
|
75
|
+
suffix="user-email" -> lê "x-amzn-bedrock-agentcore-runtime-custom-user-email"
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
suffix: Parte final do nome do header.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
O valor do header (str) ou None.
|
|
82
|
+
"""
|
|
83
|
+
return self._headers().get(f"{self.HEADER_PREFIX}-{suffix}", None)
|
|
84
|
+
|
|
85
|
+
def _to_int(self, value, default: int = 0) -> int:
|
|
86
|
+
"""
|
|
87
|
+
Converte um valor para int de forma segura.
|
|
88
|
+
|
|
89
|
+
Por quê:
|
|
90
|
+
- Headers podem vir como None, string vazia ou valores inválidos ("abc").
|
|
91
|
+
- Este método evita exceptions e padroniza o fallback para 'default'.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
value: valor a converter.
|
|
95
|
+
default: valor retornado caso não seja possível converter.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
int: valor convertido ou default.
|
|
99
|
+
"""
|
|
100
|
+
try:
|
|
101
|
+
if value is None:
|
|
102
|
+
return default
|
|
103
|
+
if isinstance(value, str) and value.strip() == "":
|
|
104
|
+
return default
|
|
105
|
+
return int(value)
|
|
106
|
+
except (TypeError, ValueError):
|
|
107
|
+
return default
|
|
108
|
+
|
|
109
|
+
def __get_from_ssm(self, parameter_name: str) -> str:
|
|
110
|
+
"""
|
|
111
|
+
Busca o valor de um parâmetro no AWS SSM Parameter Store.
|
|
112
|
+
|
|
113
|
+
Como funciona:
|
|
114
|
+
- O runtime envia um header "...-prefix" que define a "pasta" (path) base dos segredos.
|
|
115
|
+
- O nome final do parâmetro no SSM fica: "{prefix}/{parameter_name}"
|
|
116
|
+
- WithDecryption=True permite ler SecureString.
|
|
117
|
+
|
|
118
|
+
Importante:
|
|
119
|
+
- Se não houver prefix no header, retorna None (não tenta chamar AWS).
|
|
120
|
+
Isso evita chamadas inválidas como "None/MEU_PARAM".
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
parameter_name: Nome do parâmetro a ser buscado.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
str | None: Valor do parâmetro, ou None se não encontrado/sem prefix.
|
|
127
|
+
"""
|
|
128
|
+
# Obtém headers do contexto do MCP
|
|
129
|
+
headers = self._headers()
|
|
130
|
+
|
|
131
|
+
# Prefixo customizado do header que aponta para a "pasta" de segredos
|
|
132
|
+
prefix = headers.get(f"{self.HEADER_PREFIX}-prefix", None)
|
|
133
|
+
|
|
134
|
+
# Sem prefix não é possível montar o path no SSM; evita chamadas inválidas.
|
|
135
|
+
if not prefix:
|
|
136
|
+
return None
|
|
137
|
+
|
|
138
|
+
# Cria cliente SSM na região configurada (default: sa-east-1)
|
|
139
|
+
client = boto3.client("ssm", region_name=self.settings.aws_region)
|
|
140
|
+
|
|
141
|
+
try:
|
|
142
|
+
response = client.get_parameter(
|
|
143
|
+
Name=f"{prefix}/{parameter_name}",
|
|
144
|
+
WithDecryption=True,
|
|
145
|
+
)
|
|
146
|
+
except client.exceptions.ParameterNotFound:
|
|
147
|
+
# Retorna None se o parâmetro não existir no SSM
|
|
148
|
+
return None
|
|
149
|
+
|
|
150
|
+
# Estrutura da resposta: {"Parameter": {"Value": "..."}}
|
|
151
|
+
return response.get("Parameter", {}).get("Value")
|
|
152
|
+
|
|
153
|
+
def get_header(self) -> Header:
|
|
154
|
+
"""
|
|
155
|
+
Extrai e retorna um objeto Header com os principais campos do runtime.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
Header: dataclass/objeto com os campos interpretados do header.
|
|
159
|
+
"""
|
|
160
|
+
return Header(
|
|
161
|
+
# Strings (podem ser None)
|
|
162
|
+
current_host=self._h("current-host"),
|
|
163
|
+
user_email=self._h("user-email"),
|
|
164
|
+
jwt_token=self._h("jwt-token"),
|
|
165
|
+
jsessionid=self._h("jsessionid"),
|
|
166
|
+
|
|
167
|
+
# Inteiros (com fallback seguro para 0)
|
|
168
|
+
organization_id=self._to_int(self._h("organization-id"), 0),
|
|
169
|
+
codparc=self._to_int(self._h("codparc"), 0),
|
|
170
|
+
iam_user_id=self._to_int(self._h("iam-user-id"), 0),
|
|
171
|
+
|
|
172
|
+
# String (pode ser None)
|
|
173
|
+
gateway_token=self._h("gateway-token"),
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
def get_parameter(self, parameter_name: str) -> str:
|
|
177
|
+
"""
|
|
178
|
+
Recupera um parâmetro sensível/configurável.
|
|
179
|
+
|
|
180
|
+
Ordem de resolução:
|
|
181
|
+
1) Variáveis de ambiente (ideal para execução local / CI)
|
|
182
|
+
2) AWS SSM Parameter Store (ideal para produção via cofre)
|
|
183
|
+
|
|
184
|
+
Observação:
|
|
185
|
+
- Implementação evita avaliar SSM antecipadamente.
|
|
186
|
+
(Não usamos mais os.getenv(name, default_func()) porque chamaria AWS sempre.)
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
parameter_name: Nome do parâmetro.
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
str | None: valor encontrado ou None.
|
|
193
|
+
"""
|
|
194
|
+
value = self._get_from_env(parameter_name)
|
|
195
|
+
if value is not None:
|
|
196
|
+
return value
|
|
197
|
+
return self._get_from_stores(parameter_name)
|
|
198
|
+
|
|
199
|
+
def _get_from_env(self, parameter_name: str):
|
|
200
|
+
"""
|
|
201
|
+
Provider interno: busca em variáveis de ambiente do sistema.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
parameter_name: Nome do parâmetro.
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
str | None
|
|
208
|
+
"""
|
|
209
|
+
return os.getenv(parameter_name)
|
|
210
|
+
|
|
211
|
+
def _get_from_stores(self, parameter_name: str):
|
|
212
|
+
"""
|
|
213
|
+
Provider interno: busca em fontes externas (hoje apenas SSM).
|
|
214
|
+
|
|
215
|
+
Este método existe para facilitar manutenção e evolução:
|
|
216
|
+
amanhã pode incluir outros provedores (Secrets Manager, Vault, etc.)
|
|
217
|
+
sem mudar a API pública de get_parameter().
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
parameter_name: Nome do parâmetro.
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
str | None
|
|
224
|
+
"""
|
|
225
|
+
return self.__get_from_ssm(parameter_name)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
__all__ = ["BiaUtil"]
|