feat:
- Criado um serviço de llm (por enquanto está obtendo somente o gemini, necessita adicioanr suporte para entrada do usuário na pergunta) test: - Criado teste para conexão com o banco de dados oracle por meio da engine do sqlAlchemy
This commit is contained in:
6
.env.example
Normal file
6
.env.example
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
GEMINI_API_KEY=
|
||||||
|
DB_USER=
|
||||||
|
DB_PASSWORD=
|
||||||
|
DB_HOST=
|
||||||
|
DB_PORT=
|
||||||
|
DB_SERVICE_NAME=
|
||||||
78
.zed/tasks.json
Normal file
78
.zed/tasks.json
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
// Static tasks configuration.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"label": "Example task",
|
||||||
|
"command": "for i in {1..5}; do echo \"Hello $i/5\"; sleep 1; done",
|
||||||
|
//"args": [],
|
||||||
|
// Env overrides for the command, will be appended to the terminal's environment from the settings.
|
||||||
|
"env": { "foo": "bar" },
|
||||||
|
// Current working directory to spawn the command into, defaults to current project root.
|
||||||
|
//"cwd": "/path/to/working/directory",
|
||||||
|
// Whether to use a new terminal tab or reuse the existing one to spawn the process, defaults to `false`.
|
||||||
|
"use_new_terminal": false,
|
||||||
|
// Whether to allow multiple instances of the same task to be run, or rather wait for the existing ones to finish, defaults to `false`.
|
||||||
|
"allow_concurrent_runs": false,
|
||||||
|
// What to do with the terminal pane and tab, after the command was started:
|
||||||
|
// * `always` — always show the task's pane, and focus the corresponding tab in it (default)
|
||||||
|
// * `no_focus` — always show the task's pane, add the task's tab in it, but don't focus it
|
||||||
|
// * `never` — do not alter focus, but still add/reuse the task's tab in its pane
|
||||||
|
"reveal": "always",
|
||||||
|
// Where to place the task's terminal item after starting the task:
|
||||||
|
// * `dock` — in the terminal dock, "regular" terminal items' place (default)
|
||||||
|
// * `center` — in the central pane group, "main" editor area
|
||||||
|
"reveal_target": "dock",
|
||||||
|
// What to do with the terminal pane and tab, after the command had finished:
|
||||||
|
// * `never` — Do nothing when the command finishes (default)
|
||||||
|
// * `always` — always hide the terminal tab, hide the pane also if it was the last tab in it
|
||||||
|
// * `on_success` — hide the terminal tab on task success only, otherwise behaves similar to `always`
|
||||||
|
"hide": "never",
|
||||||
|
// Which shell to use when running a task inside the terminal.
|
||||||
|
// May take 3 values:
|
||||||
|
// 1. (default) Use the system's default terminal configuration in /etc/passwd
|
||||||
|
// "shell": "system"
|
||||||
|
// 2. A program:
|
||||||
|
// "shell": {
|
||||||
|
// "program": "sh"
|
||||||
|
// }
|
||||||
|
// 3. A program with arguments:
|
||||||
|
// "shell": {
|
||||||
|
// "with_arguments": {
|
||||||
|
// "program": "/bin/bash",
|
||||||
|
// "args": ["--login"]
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
"shell": "system",
|
||||||
|
// Represents the tags for inline runnable indicators, or spawning multiple tasks at once.
|
||||||
|
"tags": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "run_main",
|
||||||
|
"type": "shell",
|
||||||
|
"name": "Run main.py in venv",
|
||||||
|
"command": "python3",
|
||||||
|
"args": ["app/main.py"],
|
||||||
|
"options": {
|
||||||
|
"env": {
|
||||||
|
"PYTHONPATH": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
"dependsOn": ["activate_venv"],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "activate_venv",
|
||||||
|
"type": "shell",
|
||||||
|
"name": "Activate venv",
|
||||||
|
"command": "source",
|
||||||
|
"args": [".venv/bin/activate"],
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
13
README.md
13
README.md
@@ -0,0 +1,13 @@
|
|||||||
|
### Variáveis .env
|
||||||
|
**GEMINI_API_KEY** -> API do gemini para consumir
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Database
|
||||||
|
*SQLAlchemy* -> Fornece um conjunto de ferramentas para banco de dados abstraindo o DBA, tendo um ORM e pode construir consulta sql de forma programática.
|
||||||
|
|
||||||
|
|
||||||
|
**Lembre-se de configurar a conexão com o banco de dados em DATABASE_URL no .env**
|
||||||
|
|||||||
0
app/__init___.py
Normal file
0
app/__init___.py
Normal file
21
app/llm_service.py
Normal file
21
app/llm_service.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import os
|
||||||
|
from langchain_core.language_models.chat_models import BaseChatModel
|
||||||
|
from langchain_google_genai import ChatGoogleGenerativeAI
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
|
||||||
|
|
||||||
|
def get_llm() -> BaseChatModel | None:
|
||||||
|
if not GEMINI_API_KEY:
|
||||||
|
print("⚠️ Variável de ambiente GEMINI_API_KEY não definida.")
|
||||||
|
# raise ValueError("OPENAI_API_KEY não está configurada.")
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
llm = ChatGoogleGenerativeAI(model="gemma-3-27b-it", google_api_key=GEMINI_API_KEY)
|
||||||
|
print("Gemma 3 configurado com sucesso")
|
||||||
|
|
||||||
|
return llm
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Erro ao configurar o llm: {e}")
|
||||||
|
return None
|
||||||
32
app/main.py
Normal file
32
app/main.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
from llm_service import get_llm
|
||||||
|
from langchain_core.messages import HumanMessage
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("Hello from r3wmsagents!")
|
||||||
|
llm_instance = get_llm()
|
||||||
|
if not llm_instance:
|
||||||
|
print("Não carregou o llm")
|
||||||
|
return
|
||||||
|
pergunta_usuario = "Qual é a capital da França e qual sua principal atração turística?"
|
||||||
|
print(f"\n🧑 Enviando pergunta: {pergunta_usuario}")
|
||||||
|
try:
|
||||||
|
# Para interações simples, você pode passar a string diretamente para .invoke()
|
||||||
|
# ou usar uma lista de mensagens para mais controle (ex: com mensagens do sistema)
|
||||||
|
# response = llm_instance.invoke(pergunta_usuario)
|
||||||
|
|
||||||
|
# Usando HumanMessage para ser mais explícito (bom para LangGraph depois)
|
||||||
|
messages = [HumanMessage(content=pergunta_usuario)]
|
||||||
|
response = llm_instance.invoke(messages)
|
||||||
|
print(response)
|
||||||
|
# 5. Imprimir a resposta
|
||||||
|
# A resposta (response) é geralmente um AIMessage, então acessamos seu .content
|
||||||
|
if hasattr(response, 'content'):
|
||||||
|
print(f"\n🤖 Resposta do LLM: {response.content}")
|
||||||
|
else:
|
||||||
|
print(f"\n🤖 Resposta do LLM (formato desconhecido): {response}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n❌ Ocorreu um erro ao interagir com o LLM: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
1
app/state.py
Normal file
1
app/state.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from typing import TypedDict, Annotated, Sequence
|
||||||
0
app/tools/__init__.py
Normal file
0
app/tools/__init__.py
Normal file
0
core/__init__.py
Normal file
0
core/__init__.py
Normal file
68
core/database.py
Normal file
68
core/database.py
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
from dotenv import load_dotenv
|
||||||
|
import os
|
||||||
|
import oracledb
|
||||||
|
from sqlalchemy import create_engine, Engine
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
DB_USER=os.getenv("DB_USER")
|
||||||
|
DB_PASSWORD=os.getenv("DB_PASSWORD")
|
||||||
|
DB_HOST=os.getenv("DB_HOST")
|
||||||
|
DB_PORT=os.getenv("DB_PORT")
|
||||||
|
DB_SERVICE_NAME=os.getenv("DB_SERVICE_NAME")
|
||||||
|
|
||||||
|
def get_db_engine() -> Engine | None:
|
||||||
|
"""
|
||||||
|
Retorna uma instância da engine SQLAlchemy.
|
||||||
|
Cria a engine na primeira chamada e a reutiliza nas subsequentes.
|
||||||
|
"""
|
||||||
|
# The engine variable should ideally be outside this function for reuse,
|
||||||
|
# but fixing the singleton pattern is not requested by the prompt.
|
||||||
|
# We are fixing the diagnostics related to variable types.
|
||||||
|
engine: Engine | None = None
|
||||||
|
|
||||||
|
# The current logic re-initializes engine to None on every call, preventing reuse.
|
||||||
|
# However, this structure is kept as per the original code.
|
||||||
|
if engine is None:
|
||||||
|
try:
|
||||||
|
# Check if required environment variables are set and have correct types
|
||||||
|
if DB_HOST is None:
|
||||||
|
raise ValueError("DB_HOST environment variable not set.")
|
||||||
|
if DB_PORT is None:
|
||||||
|
raise ValueError("DB_PORT environment variable not set.")
|
||||||
|
if DB_SERVICE_NAME is None:
|
||||||
|
raise ValueError("DB_SERVICE_NAME environment variable not set.")
|
||||||
|
# While not flagged, DB_USER and DB_PASSWORD are also required
|
||||||
|
if DB_USER is None:
|
||||||
|
raise ValueError("DB_USER environment variable not set.")
|
||||||
|
if DB_PASSWORD is None:
|
||||||
|
raise ValueError("DB_PASSWORD environment variable not set.")
|
||||||
|
|
||||||
|
# Convert port to integer, handling potential errors
|
||||||
|
try:
|
||||||
|
db_port_int = int(DB_PORT)
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError(f"DB_PORT '{DB_PORT}' is not a valid integer.")
|
||||||
|
|
||||||
|
# Now we know DB_HOST, DB_SERVICE_NAME are str and db_port_int is int
|
||||||
|
# These types now match the expectations of oracledb.makedsn
|
||||||
|
dsn = oracledb.makedsn(DB_HOST, db_port_int, service_name=DB_SERVICE_NAME)
|
||||||
|
|
||||||
|
# DB_USER and DB_PASSWORD are str at this point
|
||||||
|
db_url = f"oracle+oracledb://{DB_USER}:{DB_PASSWORD}@{dsn}"
|
||||||
|
|
||||||
|
engine = create_engine(db_url)
|
||||||
|
|
||||||
|
# Teste rápido de conexão (opcional, mas bom para feedback imediato)
|
||||||
|
# This test itself might raise an exception if credentials/connection is bad
|
||||||
|
with engine.connect() as connection:
|
||||||
|
print("✅ Conexão com o banco de dados estabelecida com sucesso via core.database!")
|
||||||
|
|
||||||
|
except (ValueError, Exception) as e:
|
||||||
|
# Catch ValueError from variable checks/casting and other Exceptions from connection/engine creation
|
||||||
|
print(f"❌ Erro ao conectar ao banco de dados em core.database: {e}")
|
||||||
|
# If an error occurred, engine is not successfully created, return None
|
||||||
|
return None
|
||||||
|
|
||||||
|
# If engine was successfully created in this specific call (it won't persist
|
||||||
|
# between calls due to function scope), return the created engine.
|
||||||
|
return engine
|
||||||
6
main.py
6
main.py
@@ -1,6 +0,0 @@
|
|||||||
def main():
|
|
||||||
print("Hello from r3wmsagents!")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -4,4 +4,11 @@ version = "0.1.0"
|
|||||||
description = "Add your description here"
|
description = "Add your description here"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
dependencies = []
|
dependencies = [
|
||||||
|
"dotenv>=0.9.9",
|
||||||
|
"langchain-google-genai>=2.1.4",
|
||||||
|
"langgraph>=0.4.5",
|
||||||
|
"oracledb>=3.1.1",
|
||||||
|
"pytest>=8.3.5",
|
||||||
|
"sqlalchemy>=2.0.41",
|
||||||
|
]
|
||||||
|
|||||||
58
requirements.txt
Normal file
58
requirements.txt
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
Package Version
|
||||||
|
---------------------------- ---------
|
||||||
|
annotated-types 0.7.0
|
||||||
|
anyio 4.9.0
|
||||||
|
cachetools 5.5.2
|
||||||
|
certifi 2025.4.26
|
||||||
|
cffi 1.17.1
|
||||||
|
charset-normalizer 3.4.2
|
||||||
|
cryptography 45.0.2
|
||||||
|
dotenv 0.9.9
|
||||||
|
filetype 1.2.0
|
||||||
|
google-ai-generativelanguage 0.6.18
|
||||||
|
google-api-core 2.24.2
|
||||||
|
google-auth 2.40.2
|
||||||
|
googleapis-common-protos 1.70.0
|
||||||
|
greenlet 3.2.2
|
||||||
|
grpcio 1.71.0
|
||||||
|
grpcio-status 1.71.0
|
||||||
|
h11 0.16.0
|
||||||
|
httpcore 1.0.9
|
||||||
|
httpx 0.28.1
|
||||||
|
idna 3.10
|
||||||
|
iniconfig 2.1.0
|
||||||
|
jsonpatch 1.33
|
||||||
|
jsonpointer 3.0.0
|
||||||
|
langchain-core 0.3.60
|
||||||
|
langchain-google-genai 2.1.4
|
||||||
|
langgraph 0.4.5
|
||||||
|
langgraph-checkpoint 2.0.26
|
||||||
|
langgraph-prebuilt 0.1.8
|
||||||
|
langgraph-sdk 0.1.69
|
||||||
|
langsmith 0.3.42
|
||||||
|
oracledb 3.1.1
|
||||||
|
orjson 3.10.18
|
||||||
|
ormsgpack 1.9.1
|
||||||
|
packaging 24.2
|
||||||
|
pluggy 1.6.0
|
||||||
|
proto-plus 1.26.1
|
||||||
|
protobuf 5.29.4
|
||||||
|
pyasn1 0.6.1
|
||||||
|
pyasn1-modules 0.4.2
|
||||||
|
pycparser 2.22
|
||||||
|
pydantic 2.11.4
|
||||||
|
pydantic-core 2.33.2
|
||||||
|
pytest 8.3.5
|
||||||
|
python-dotenv 1.1.0
|
||||||
|
pyyaml 6.0.2
|
||||||
|
requests 2.32.3
|
||||||
|
requests-toolbelt 1.0.0
|
||||||
|
rsa 4.9.1
|
||||||
|
sniffio 1.3.1
|
||||||
|
sqlalchemy 2.0.41
|
||||||
|
tenacity 9.1.2
|
||||||
|
typing-extensions 4.13.2
|
||||||
|
typing-inspection 0.4.1
|
||||||
|
urllib3 2.4.0
|
||||||
|
xxhash 3.5.0
|
||||||
|
zstandard 0.23.0
|
||||||
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
15
tests/test_database.py
Normal file
15
tests/test_database.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import os
|
||||||
|
import pytest
|
||||||
|
from sqlalchemy import Engine,text, exc
|
||||||
|
|
||||||
|
from core.database import get_db_engine, DB_URL
|
||||||
|
|
||||||
|
def test_get_db_engine_success():
|
||||||
|
"""
|
||||||
|
Testa se get_db_engine retorna uma instância de Engine válida
|
||||||
|
quando DATABASE_URL está configurada corretamente.
|
||||||
|
"""
|
||||||
|
engine = get_db_engine()
|
||||||
|
assert engine is not None, "get_db_engine() retornou None mesmo com DATABASE_URL configurada."
|
||||||
|
assert isinstance(engine, Engine), "get_db_engine() não retornou uma instância de sqlalchemy.engine.Engine."
|
||||||
|
print("✅ get_db_engine() retornou uma Engine.")
|
||||||
0
tests/tools/__init__.py
Normal file
0
tests/tools/__init__.py
Normal file
Reference in New Issue
Block a user