diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e7d218c..1a30a46 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,13 +20,12 @@ jobs: runs-on: ubuntu-latest outputs: bursatil: ${{ steps.filter.outputs.bursatil }} - ChatEgresos: ${{ steps.filter.outputs.ChatEgresos }} + egresos: ${{ steps.filter.outputs.egresos }} inversionistas: ${{ steps.filter.outputs.inversionistas }} normativa: ${{ steps.filter.outputs.normativa }} ocp: ${{ steps.filter.outputs.ocp }} pyme: ${{ steps.filter.outputs.pyme }} riesgos: ${{ steps.filter.outputs.riesgos }} - Test: ${{ steps.filter.outputs.Test }} voz-del-cliente: ${{ steps.filter.outputs.voz-del-cliente }} steps: - uses: actions/checkout@v4 @@ -39,8 +38,8 @@ jobs: filters: | bursatil: - 'apps/bursatil/**' - ChatEgresos: - - 'apps/ChatEgresos/**' + egresos: + - 'apps/egresos/**' inversionistas: - 'apps/inversionistas/**' normativa: @@ -51,8 +50,6 @@ jobs: - 'apps/pyme/**' riesgos: - 'apps/riesgos/**' - Test: - - 'apps/Test/**' voz-del-cliente: - 'apps/voz-del-cliente/**' @@ -80,9 +77,9 @@ jobs: build-args: | PACKAGE=bursatil - build-ChatEgresos: + build-egresos: needs: detect-changes - if: needs.detect-changes.outputs.ChatEgresos == 'true' + if: needs.detect-changes.outputs.egresos == 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -99,10 +96,10 @@ jobs: file: .containers/python/Dockerfile push: true tags: | - ${{ env.REGISTRY }}/${{ env.REGISTRY_PATH }}-ChatEgresos:latest - ${{ env.REGISTRY }}/${{ env.REGISTRY_PATH }}-ChatEgresos:${{ github.sha }} + ${{ env.REGISTRY }}/${{ env.REGISTRY_PATH }}-egresos:latest + ${{ env.REGISTRY }}/${{ env.REGISTRY_PATH }}-egresos:${{ github.sha }} build-args: | - PACKAGE=ChatEgresos + PACKAGE=egresos build-inversionistas: needs: detect-changes @@ -224,30 +221,6 @@ jobs: build-args: | PACKAGE=riesgos - build-Test: - needs: detect-changes - if: needs.detect-changes.outputs.Test == 'true' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Log in to Gitea Registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ secrets.REGISTRY_USERNAME }} - password: ${{ secrets.REGISTRY_PASSWORD }} - - name: Build and push - uses: docker/build-push-action@v5 - with: - context: . - file: .containers/python/Dockerfile - push: true - tags: | - ${{ env.REGISTRY }}/${{ env.REGISTRY_PATH }}-Test:latest - ${{ env.REGISTRY }}/${{ env.REGISTRY_PATH }}-Test:${{ github.sha }} - build-args: | - PACKAGE=Test - build-voz-del-cliente: needs: detect-changes if: needs.detect-changes.outputs.voz-del-cliente == 'true' diff --git a/apps/Test/.eslintrc.cjs b/apps/Test/.eslintrc.cjs deleted file mode 100644 index d6c9537..0000000 --- a/apps/Test/.eslintrc.cjs +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = { - root: true, - env: { browser: true, es2020: true }, - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:react-hooks/recommended', - ], - ignorePatterns: ['dist', '.eslintrc.cjs'], - parser: '@typescript-eslint/parser', - plugins: ['react-refresh'], - rules: { - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, - ], - }, -} diff --git a/apps/Test/gui/App.tsx b/apps/Test/gui/App.tsx deleted file mode 100644 index caf827a..0000000 --- a/apps/Test/gui/App.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { Chat, ChatSidebar } from "@banorte/chat-ui"; -import { messageStore } from "./store/messageStore"; -import { conversationStore } from "./store/conversationStore"; -import { httpRequest } from "./utils/request"; - -// Assets -import banorteLogo from "./assets/banortelogo.png"; -import sidebarMaya from "./assets/sidebar_maya_contigo.png"; -import brujulaElipse from "./assets/brujula_elipse.png"; -import sendIcon from "./assets/chat_maya_boton_enviar.png"; -import userAvatar from "./assets/chat_maya_default_avatar.png"; -import botAvatar from "./assets/brujula.png"; - -function App() { - const { messages, pushMessage } = messageStore(); - const { - conversationId, - setConversationId, - setAssistantName, - receivingMsg, - setReceivingMsg - } = conversationStore(); - - const handleStartConversation = async (user: string, assistant: string): Promise => { - const response = await httpRequest("POST", "/v1/conversation", { user, assistant }); - console.log("Conversation id:", response.conversation_id); - return response.conversation_id; - }; - - const handleFeedback = async (key: string, rating: string): Promise => { - await httpRequest("POST", "/v1/feedback", { key, rating }); - }; - - const assistant = "Maya" + "Test"; - - return ( -
-
- - -
-
- ); -} - -export default App; diff --git a/apps/Test/gui/assets/banortelogo.png b/apps/Test/gui/assets/banortelogo.png deleted file mode 100644 index 86bbe95..0000000 Binary files a/apps/Test/gui/assets/banortelogo.png and /dev/null differ diff --git a/apps/Test/gui/assets/brujula.png b/apps/Test/gui/assets/brujula.png deleted file mode 100644 index 9d9bf07..0000000 Binary files a/apps/Test/gui/assets/brujula.png and /dev/null differ diff --git a/apps/Test/gui/assets/brujula_elipse.png b/apps/Test/gui/assets/brujula_elipse.png deleted file mode 100644 index 0b57ceb..0000000 Binary files a/apps/Test/gui/assets/brujula_elipse.png and /dev/null differ diff --git a/apps/Test/gui/assets/chat_maya_boton_enviar.png b/apps/Test/gui/assets/chat_maya_boton_enviar.png deleted file mode 100644 index c2c9c56..0000000 Binary files a/apps/Test/gui/assets/chat_maya_boton_enviar.png and /dev/null differ diff --git a/apps/Test/gui/assets/chat_maya_default_avatar.png b/apps/Test/gui/assets/chat_maya_default_avatar.png deleted file mode 100644 index ed9da7f..0000000 Binary files a/apps/Test/gui/assets/chat_maya_default_avatar.png and /dev/null differ diff --git a/apps/Test/gui/assets/sidebar_maya_contigo.png b/apps/Test/gui/assets/sidebar_maya_contigo.png deleted file mode 100644 index 28dcc6c..0000000 Binary files a/apps/Test/gui/assets/sidebar_maya_contigo.png and /dev/null differ diff --git a/apps/Test/gui/index.css b/apps/Test/gui/index.css deleted file mode 100644 index 903f821..0000000 --- a/apps/Test/gui/index.css +++ /dev/null @@ -1,16 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -.markdown a { - color: #0000FF; - text-decoration: underline; -} - -.markdown a:hover { - color: #FF0000; -} - -.markdown a:visited { - color: #800080; -} \ No newline at end of file diff --git a/apps/Test/gui/main.tsx b/apps/Test/gui/main.tsx deleted file mode 100644 index eb0a604..0000000 --- a/apps/Test/gui/main.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import ReactDOM from "react-dom/client"; -import App from "./App.tsx"; -import "./index.css"; - -ReactDOM.createRoot(document.getElementById("root")!).render(); diff --git a/apps/Test/gui/store/conversationStore.ts b/apps/Test/gui/store/conversationStore.ts deleted file mode 100644 index b1b10a8..0000000 --- a/apps/Test/gui/store/conversationStore.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { create } from "zustand"; - -interface conversationState { - assistantName: string; - conversationId: string; - receivingMsg: boolean; - setConversationId: (newId: string) => void; - setAssistantName: (newName: string) => void; - setReceivingMsg: (newState: boolean) => void; -} - -export const conversationStore = create()((set) => ({ - assistantName: "", - conversationId: "", - receivingMsg: false, - setConversationId: (newId) => set({ conversationId: newId }), - setAssistantName: (newName) => set({ assistantName: newName }), - setReceivingMsg: (newState) => set({ receivingMsg: newState }), -})); diff --git a/apps/Test/gui/store/messageStore.ts b/apps/Test/gui/store/messageStore.ts deleted file mode 100644 index 852c1e7..0000000 --- a/apps/Test/gui/store/messageStore.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { create } from "zustand"; - -interface messageState { - messages: Array<{ user: boolean; content: string }>; - pushMessage: (newMessage: { user: boolean; content: string }) => void; - resetConversation: () => void; -} - -export const messageStore = create()((set) => ({ - messages: [], - pushMessage: (newMessage) => - set((state) => ({ messages: [...state.messages, newMessage] })), - resetConversation: () => set(() => ({ messages: [] })), -})); diff --git a/apps/Test/gui/utils/request.ts b/apps/Test/gui/utils/request.ts deleted file mode 100644 index 510253d..0000000 --- a/apps/Test/gui/utils/request.ts +++ /dev/null @@ -1,16 +0,0 @@ -export async function httpRequest( - method: string, - endpoint: string, - body: object | null, -) { - const url = "/api" + endpoint; - const data = { - method: method, - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(body), - credentials: "include" as RequestCredentials, - }; - return await fetch(url, data).then((response) => response.json()); -} diff --git a/apps/Test/gui/vite-env.d.ts b/apps/Test/gui/vite-env.d.ts deleted file mode 100644 index 11f02fe..0000000 --- a/apps/Test/gui/vite-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/apps/Test/index.html b/apps/Test/index.html deleted file mode 100644 index fa5f058..0000000 --- a/apps/Test/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - Test - - -
- - - diff --git a/apps/Test/package.json b/apps/Test/package.json deleted file mode 100644 index 1432b98..0000000 --- a/apps/Test/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "Test", - "private": true, - "version": "0.0.7", - "type": "module", - "scripts": { - "dev": "vite", - "build": "tsc && vite build", - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview" - }, - "dependencies": { - "@banorte/chat-ui": "workspace:*", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-markdown": "^9.0.1", - "react-spring": "^9.7.4", - "rehype-raw": "^7.0.0", - "sse.js": "^2.5.0", - "zustand": "^4.5.2" - }, - "devDependencies": { - "@iconify-icon/react": "^2.1.0", - "@types/react": "^18.2.67", - "@types/react-dom": "^18.2.22", - "@typescript-eslint/eslint-plugin": "^7.3.1", - "@typescript-eslint/parser": "^7.3.1", - "@vitejs/plugin-react": "^4.2.1", - "autoprefixer": "^10.4.19", - "daisyui": "^4.7.3", - "eslint": "^8.57.0", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.6", - "postcss": "^8.4.38", - "tailwind-scrollbar": "^3.1.0", - "tailwindcss": "^3.4.1", - "typescript": "^5.4.3", - "vite": "^5.2.3" - } -} diff --git a/apps/Test/postcss.config.js b/apps/Test/postcss.config.js deleted file mode 100644 index 2e7af2b..0000000 --- a/apps/Test/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -export default { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} diff --git a/apps/Test/pyproject.toml b/apps/Test/pyproject.toml deleted file mode 100644 index 2d3973d..0000000 --- a/apps/Test/pyproject.toml +++ /dev/null @@ -1,18 +0,0 @@ -[project] -name = "Test" -version = "0.1.0" -description = "Add your description here" -readme = "README.md" -requires-python = ">=3.12, <4" -dependencies = [ - "aiohttp>=3.11.16", - "fastapi>=0.115.6", - "hvac>=2.3.0", - "langchain-azure-ai[opentelemetry]>=0.1.4", - "mongo-memory", - "pydantic-settings>=2.8.1", - "uvicorn>=0.34.0", -] - -[tool.uv.sources] -mongo-memory = { workspace = true } diff --git a/apps/Test/tailwind.config.js b/apps/Test/tailwind.config.js deleted file mode 100644 index 1e964c3..0000000 --- a/apps/Test/tailwind.config.js +++ /dev/null @@ -1,27 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -export default { - content: ["./index.html", "./gui/**/*.{js,ts,jsx,tsx}"], - theme: { - extend: { - backgroundImage: { - "navigation-pattern": "url('./assets/navigation.webp')", - }, - }, - }, - plugins: [ - require("daisyui"), - require("tailwind-scrollbar"), - require("@banorte/chat-ui/tailwind") - ], - daisyui: { - themes: [ - { - light: { - ...require("daisyui/src/theming/themes")["light"], - primary: "red", - secondary: "teal", - }, - }, - ], - }, -}; diff --git a/apps/Test/tsconfig.json b/apps/Test/tsconfig.json deleted file mode 100644 index 2f0d3e5..0000000 --- a/apps/Test/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2023", - "useDefineForClassFields": true, - "lib": ["ES2023", "DOM", "DOM.Iterable", "ES2021.String"], - "module": "ESNext", - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx", - - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true - }, - "include": ["gui"], - "references": [{ "path": "./tsconfig.node.json" }] -} diff --git a/apps/Test/tsconfig.node.json b/apps/Test/tsconfig.node.json deleted file mode 100644 index 97ede7e..0000000 --- a/apps/Test/tsconfig.node.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "compilerOptions": { - "composite": true, - "skipLibCheck": true, - "module": "ESNext", - "moduleResolution": "bundler", - "allowSyntheticDefaultImports": true, - "strict": true - }, - "include": ["vite.config.ts"] -} diff --git a/apps/Test/vite.config.ts b/apps/Test/vite.config.ts deleted file mode 100644 index 58f5a85..0000000 --- a/apps/Test/vite.config.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { defineConfig } from "vite"; -import react from "@vitejs/plugin-react"; - -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [react()], - server: { - host: "0.0.0.0", - port: 3000, - proxy: { - "/api": { - target: "http://localhost:8000", - }, - }, - allowedHosts: true, - }, -}); diff --git a/apps/bursatil/.k8s/deployment.yaml b/apps/bursatil/.k8s/deployment.yaml new file mode 100644 index 0000000..c9ed2eb --- /dev/null +++ b/apps/bursatil/.k8s/deployment.yaml @@ -0,0 +1,42 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mayacontigo-bursatil + namespace: apps + labels: + app: mayacontigo-bursatil +spec: + replicas: 1 + selector: + matchLabels: + app: mayacontigo-bursatil + template: + metadata: + labels: + app: mayacontigo-bursatil + spec: + imagePullSecrets: + - name: gitea-registry-cred + containers: + - name: mayacontigo-bursatil + image: gitea.ia-innovacion.work/innovacion/mayacontigo-bursatil:latest + env: + - name: VAULT_TOKEN + valueFrom: + secretKeyRef: + name: mayacontigo-bursatil-secret + key: VAULT_TOKEN + ports: + - containerPort: 80 + readinessProbe: + httpGet: + path: / + port: 80 + initialDelaySeconds: 5 + periodSeconds: 10 + livenessProbe: + httpGet: + path: / + port: 80 + initialDelaySeconds: 15 + periodSeconds: 20 diff --git a/apps/bursatil/.k8s/ingress.yaml b/apps/bursatil/.k8s/ingress.yaml new file mode 100644 index 0000000..09ce6e1 --- /dev/null +++ b/apps/bursatil/.k8s/ingress.yaml @@ -0,0 +1,19 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: mayacontigo-bursatil-ingress + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + ingressClassName: nginx + rules: + - host: mayacontigo-bursatil.app.ia-innovacion.work + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: mayacontigo-bursatil-service + port: + number: 80 diff --git a/apps/bursatil/.k8s/secrets.yaml b/apps/bursatil/.k8s/secrets.yaml new file mode 100644 index 0000000..f075c5f --- /dev/null +++ b/apps/bursatil/.k8s/secrets.yaml @@ -0,0 +1,17 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: mayacontigo-bursatil-vault + namespace: apps +spec: + refreshInterval: "15s" + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + target: + name: mayacontigo-bursatil-secret + data: + - secretKey: VAULT_TOKEN + remoteRef: + key: mayacontigo-bursatil + property: VAULT_TOKEN diff --git a/apps/bursatil/.k8s/service.yaml b/apps/bursatil/.k8s/service.yaml new file mode 100644 index 0000000..70a9957 --- /dev/null +++ b/apps/bursatil/.k8s/service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: mayacontigo-bursatil-service + labels: + app: mayacontigo-bursatil +spec: + selector: + app: mayacontigo-bursatil + ports: + - port: 80 + targetPort: 80 + protocol: TCP + type: ClusterIP diff --git a/apps/ChatEgresos/.eslintrc.cjs b/apps/egresos/.eslintrc.cjs similarity index 100% rename from apps/ChatEgresos/.eslintrc.cjs rename to apps/egresos/.eslintrc.cjs diff --git a/apps/egresos/.k8s/deployment.yaml b/apps/egresos/.k8s/deployment.yaml new file mode 100644 index 0000000..8bbf1ee --- /dev/null +++ b/apps/egresos/.k8s/deployment.yaml @@ -0,0 +1,42 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mayacontigo-egresos + namespace: apps + labels: + app: mayacontigo-egresos +spec: + replicas: 1 + selector: + matchLabels: + app: mayacontigo-egresos + template: + metadata: + labels: + app: mayacontigo-egresos + spec: + imagePullSecrets: + - name: gitea-registry-cred + containers: + - name: mayacontigo-egresos + image: gitea.ia-innovacion.work/innovacion/mayacontigo-egresos:latest + env: + - name: VAULT_TOKEN + valueFrom: + secretKeyRef: + name: mayacontigo-egresos-secret + key: VAULT_TOKEN + ports: + - containerPort: 80 + readinessProbe: + httpGet: + path: / + port: 80 + initialDelaySeconds: 5 + periodSeconds: 10 + livenessProbe: + httpGet: + path: / + port: 80 + initialDelaySeconds: 15 + periodSeconds: 20 diff --git a/apps/egresos/.k8s/ingress.yaml b/apps/egresos/.k8s/ingress.yaml new file mode 100644 index 0000000..135133d --- /dev/null +++ b/apps/egresos/.k8s/ingress.yaml @@ -0,0 +1,19 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: mayacontigo-egresos-ingress + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + ingressClassName: nginx + rules: + - host: mayacontigo-egresos.app.ia-innovacion.work + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: mayacontigo-egresos-service + port: + number: 80 diff --git a/apps/egresos/.k8s/secrets.yaml b/apps/egresos/.k8s/secrets.yaml new file mode 100644 index 0000000..949e669 --- /dev/null +++ b/apps/egresos/.k8s/secrets.yaml @@ -0,0 +1,17 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: mayacontigo-egresos-vault + namespace: apps +spec: + refreshInterval: "15s" + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + target: + name: mayacontigo-egresos-secret + data: + - secretKey: VAULT_TOKEN + remoteRef: + key: mayacontigo-egresos + property: VAULT_TOKEN diff --git a/apps/egresos/.k8s/service.yaml b/apps/egresos/.k8s/service.yaml new file mode 100644 index 0000000..840cbd5 --- /dev/null +++ b/apps/egresos/.k8s/service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: mayacontigo-egresos-service + labels: + app: mayacontigo-egresos +spec: + selector: + app: mayacontigo-egresos + ports: + - port: 80 + targetPort: 80 + protocol: TCP + type: ClusterIP diff --git a/apps/ChatEgresos/api/__init__.py b/apps/egresos/api/__init__.py similarity index 100% rename from apps/ChatEgresos/api/__init__.py rename to apps/egresos/api/__init__.py diff --git a/apps/ChatEgresos/api/agent/__init__.py b/apps/egresos/api/agent/__init__.py similarity index 100% rename from apps/ChatEgresos/api/agent/__init__.py rename to apps/egresos/api/agent/__init__.py diff --git a/apps/ChatEgresos/api/agent/main.py b/apps/egresos/api/agent/main.py similarity index 100% rename from apps/ChatEgresos/api/agent/main.py rename to apps/egresos/api/agent/main.py diff --git a/apps/ChatEgresos/api/agent/system_prompt.md b/apps/egresos/api/agent/system_prompt.md similarity index 100% rename from apps/ChatEgresos/api/agent/system_prompt.md rename to apps/egresos/api/agent/system_prompt.md diff --git a/apps/ChatEgresos/api/config.py b/apps/egresos/api/config.py similarity index 100% rename from apps/ChatEgresos/api/config.py rename to apps/egresos/api/config.py diff --git a/apps/ChatEgresos/api/context.py b/apps/egresos/api/context.py similarity index 100% rename from apps/ChatEgresos/api/context.py rename to apps/egresos/api/context.py diff --git a/apps/ChatEgresos/api/server.py b/apps/egresos/api/server.py similarity index 74% rename from apps/ChatEgresos/api/server.py rename to apps/egresos/api/server.py index f418840..917279b 100644 --- a/apps/ChatEgresos/api/server.py +++ b/apps/egresos/api/server.py @@ -1,11 +1,11 @@ -import uuid import time +import uuid from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.responses import StreamingResponse -from pydantic import BaseModel from langfuse import Langfuse +from pydantic import BaseModel from api import services from api.agent import Agent @@ -14,10 +14,11 @@ from api.config import config # Configurar Langfuse langfuse = Langfuse( public_key="pk-lf-49cb04b3-0c7d-475b-8105-ad8b8749ecdd", - secret_key="sk-lf-e02fa322-c709-4d80-bef2-9cb279846a0c", - host="https://ailogger.azurewebsites.net" + secret_key="sk-lf-e02fa322-c709-4d80-bef2-9cb279846a0c", + host="https://ailogger.azurewebsites.net", ) + @asynccontextmanager async def lifespan(_: FastAPI): await config.init_mongo_db() @@ -39,6 +40,7 @@ class Message(BaseModel): conversation_id: uuid.UUID prompt: str + @app.post("/api/v1/message") async def send(message: Message): # Crear trace principal @@ -47,66 +49,68 @@ async def send(message: Message): session_id=str(message.conversation_id), input={ "prompt": message.prompt, - "conversation_id": str(message.conversation_id) - } + "conversation_id": str(message.conversation_id), + }, ) - + def b64_sse(func): async def wrapper(*args, **kwargs): response_parts = [] start_time = time.time() - + async for chunk in func(*args, **kwargs): if chunk.type == "text" and chunk.content: response_parts.append(str(chunk.content)) - + content = chunk.model_dump_json() data = f"data: {content}\n\n" yield data - + end_time = time.time() - latency_ms = round((end_time - start_time) * 1000) + latency_ms = round((end_time - start_time) * 1000) full_response = "".join(response_parts) - - - input_tokens = len(message.prompt.split()) * 1.3 + + input_tokens = len(message.prompt.split()) * 1.3 output_tokens = len(full_response.split()) * 1.3 total_tokens = int(input_tokens + output_tokens) - - - cost_per_1k_input = 0.03 - cost_per_1k_output = 0.06 - total_cost = (input_tokens/1000 * cost_per_1k_input) + (output_tokens/1000 * cost_per_1k_output) - - + + cost_per_1k_input = 0.03 + cost_per_1k_output = 0.06 + total_cost = (input_tokens / 1000 * cost_per_1k_input) + ( + output_tokens / 1000 * cost_per_1k_output + ) + trace.update( output={"response": full_response}, usage={ "input": int(input_tokens), - "output": int(output_tokens), + "output": int(output_tokens), "total": total_tokens, - "unit": "TOKENS" - } + "unit": "TOKENS", + }, ) - - + langfuse.score( trace_id=trace.id, name="latency", value=latency_ms, - comment=f"Response time: {latency_ms}ms" + comment=f"Response time: {latency_ms}ms", ) - - + langfuse.score( trace_id=trace.id, name="cost", value=round(total_cost, 4), - comment=f"Estimated cost: ${round(total_cost, 4)}" + comment=f"Estimated cost: ${round(total_cost, 4)}", ) return wrapper sse_stream = b64_sse(services.stream) generator = sse_stream(agent, message.prompt, message.conversation_id) - return StreamingResponse(generator, media_type="text/event-stream") \ No newline at end of file + return StreamingResponse(generator, media_type="text/event-stream") + + +@app.get("/") +async def health(): + return {"status": "ok"} diff --git a/apps/ChatEgresos/api/services/__init__.py b/apps/egresos/api/services/__init__.py similarity index 100% rename from apps/ChatEgresos/api/services/__init__.py rename to apps/egresos/api/services/__init__.py diff --git a/apps/ChatEgresos/api/services/stream_response.py b/apps/egresos/api/services/stream_response.py similarity index 100% rename from apps/ChatEgresos/api/services/stream_response.py rename to apps/egresos/api/services/stream_response.py diff --git a/apps/ChatEgresos/gui/App.tsx b/apps/egresos/gui/App.tsx similarity index 100% rename from apps/ChatEgresos/gui/App.tsx rename to apps/egresos/gui/App.tsx diff --git a/apps/ChatEgresos/gui/assets/banortelogo.png b/apps/egresos/gui/assets/banortelogo.png similarity index 100% rename from apps/ChatEgresos/gui/assets/banortelogo.png rename to apps/egresos/gui/assets/banortelogo.png diff --git a/apps/ChatEgresos/gui/assets/brujula.png b/apps/egresos/gui/assets/brujula.png similarity index 100% rename from apps/ChatEgresos/gui/assets/brujula.png rename to apps/egresos/gui/assets/brujula.png diff --git a/apps/ChatEgresos/gui/assets/brujula_elipse.png b/apps/egresos/gui/assets/brujula_elipse.png similarity index 100% rename from apps/ChatEgresos/gui/assets/brujula_elipse.png rename to apps/egresos/gui/assets/brujula_elipse.png diff --git a/apps/ChatEgresos/gui/assets/chat_maya_boton_enviar.png b/apps/egresos/gui/assets/chat_maya_boton_enviar.png similarity index 100% rename from apps/ChatEgresos/gui/assets/chat_maya_boton_enviar.png rename to apps/egresos/gui/assets/chat_maya_boton_enviar.png diff --git a/apps/ChatEgresos/gui/assets/chat_maya_default_avatar.png b/apps/egresos/gui/assets/chat_maya_default_avatar.png similarity index 100% rename from apps/ChatEgresos/gui/assets/chat_maya_default_avatar.png rename to apps/egresos/gui/assets/chat_maya_default_avatar.png diff --git a/apps/ChatEgresos/gui/assets/sidebar_maya_contigo.png b/apps/egresos/gui/assets/sidebar_maya_contigo.png similarity index 100% rename from apps/ChatEgresos/gui/assets/sidebar_maya_contigo.png rename to apps/egresos/gui/assets/sidebar_maya_contigo.png diff --git a/apps/ChatEgresos/gui/index.css b/apps/egresos/gui/index.css similarity index 100% rename from apps/ChatEgresos/gui/index.css rename to apps/egresos/gui/index.css diff --git a/apps/ChatEgresos/gui/main.tsx b/apps/egresos/gui/main.tsx similarity index 100% rename from apps/ChatEgresos/gui/main.tsx rename to apps/egresos/gui/main.tsx diff --git a/apps/ChatEgresos/gui/store/conversationStore.ts b/apps/egresos/gui/store/conversationStore.ts similarity index 100% rename from apps/ChatEgresos/gui/store/conversationStore.ts rename to apps/egresos/gui/store/conversationStore.ts diff --git a/apps/ChatEgresos/gui/store/messageStore.ts b/apps/egresos/gui/store/messageStore.ts similarity index 100% rename from apps/ChatEgresos/gui/store/messageStore.ts rename to apps/egresos/gui/store/messageStore.ts diff --git a/apps/ChatEgresos/gui/utils/request.ts b/apps/egresos/gui/utils/request.ts similarity index 100% rename from apps/ChatEgresos/gui/utils/request.ts rename to apps/egresos/gui/utils/request.ts diff --git a/apps/ChatEgresos/gui/vite-env.d.ts b/apps/egresos/gui/vite-env.d.ts similarity index 100% rename from apps/ChatEgresos/gui/vite-env.d.ts rename to apps/egresos/gui/vite-env.d.ts diff --git a/apps/ChatEgresos/index.html b/apps/egresos/index.html similarity index 100% rename from apps/ChatEgresos/index.html rename to apps/egresos/index.html diff --git a/apps/ChatEgresos/package.json b/apps/egresos/package.json similarity index 100% rename from apps/ChatEgresos/package.json rename to apps/egresos/package.json diff --git a/apps/ChatEgresos/postcss.config.js b/apps/egresos/postcss.config.js similarity index 100% rename from apps/ChatEgresos/postcss.config.js rename to apps/egresos/postcss.config.js diff --git a/apps/ChatEgresos/pyproject.toml b/apps/egresos/pyproject.toml similarity index 95% rename from apps/ChatEgresos/pyproject.toml rename to apps/egresos/pyproject.toml index 9a1eee5..d90441b 100644 --- a/apps/ChatEgresos/pyproject.toml +++ b/apps/egresos/pyproject.toml @@ -1,5 +1,5 @@ [project] -name = "ChatEgresos" +name = "egresos" version = "0.1.0" description = "Add your description here" readme = "README.md" diff --git a/apps/ChatEgresos/readme.md b/apps/egresos/readme.md similarity index 100% rename from apps/ChatEgresos/readme.md rename to apps/egresos/readme.md diff --git a/apps/ChatEgresos/tailwind.config.js b/apps/egresos/tailwind.config.js similarity index 100% rename from apps/ChatEgresos/tailwind.config.js rename to apps/egresos/tailwind.config.js diff --git a/apps/ChatEgresos/tsconfig.json b/apps/egresos/tsconfig.json similarity index 100% rename from apps/ChatEgresos/tsconfig.json rename to apps/egresos/tsconfig.json diff --git a/apps/ChatEgresos/tsconfig.node.json b/apps/egresos/tsconfig.node.json similarity index 100% rename from apps/ChatEgresos/tsconfig.node.json rename to apps/egresos/tsconfig.node.json diff --git a/apps/ChatEgresos/vite.config.ts b/apps/egresos/vite.config.ts similarity index 100% rename from apps/ChatEgresos/vite.config.ts rename to apps/egresos/vite.config.ts diff --git a/apps/normativa/.k8s/deployment.yaml b/apps/normativa/.k8s/deployment.yaml new file mode 100644 index 0000000..7196868 --- /dev/null +++ b/apps/normativa/.k8s/deployment.yaml @@ -0,0 +1,42 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mayacontigo-normativa + namespace: apps + labels: + app: mayacontigo-normativa +spec: + replicas: 1 + selector: + matchLabels: + app: mayacontigo-normativa + template: + metadata: + labels: + app: mayacontigo-normativa + spec: + imagePullSecrets: + - name: gitea-registry-cred + containers: + - name: mayacontigo-normativa + image: gitea.ia-innovacion.work/innovacion/mayacontigo-normativa:latest + env: + - name: VAULT_TOKEN + valueFrom: + secretKeyRef: + name: mayacontigo-normativa-secret + key: VAULT_TOKEN + ports: + - containerPort: 80 + readinessProbe: + httpGet: + path: / + port: 80 + initialDelaySeconds: 5 + periodSeconds: 10 + livenessProbe: + httpGet: + path: / + port: 80 + initialDelaySeconds: 15 + periodSeconds: 20 diff --git a/apps/normativa/.k8s/ingress.yaml b/apps/normativa/.k8s/ingress.yaml new file mode 100644 index 0000000..d57c033 --- /dev/null +++ b/apps/normativa/.k8s/ingress.yaml @@ -0,0 +1,19 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: mayacontigo-normativa-ingress + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + ingressClassName: nginx + rules: + - host: mayacontigo-normativa.app.ia-innovacion.work + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: mayacontigo-normativa-service + port: + number: 80 diff --git a/apps/normativa/.k8s/secrets.yaml b/apps/normativa/.k8s/secrets.yaml new file mode 100644 index 0000000..4c9f3eb --- /dev/null +++ b/apps/normativa/.k8s/secrets.yaml @@ -0,0 +1,17 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: mayacontigo-normativa-vault + namespace: apps +spec: + refreshInterval: "15s" + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + target: + name: mayacontigo-normativa-secret + data: + - secretKey: VAULT_TOKEN + remoteRef: + key: mayacontigo-normativa + property: VAULT_TOKEN diff --git a/apps/normativa/.k8s/service.yaml b/apps/normativa/.k8s/service.yaml new file mode 100644 index 0000000..11c7278 --- /dev/null +++ b/apps/normativa/.k8s/service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: mayacontigo-normativa-service + labels: + app: mayacontigo-normativa +spec: + selector: + app: mayacontigo-normativa + ports: + - port: 80 + targetPort: 80 + protocol: TCP + type: ClusterIP diff --git a/apps/normativa/api/server.py b/apps/normativa/api/server.py index c466594..29f7abd 100644 --- a/apps/normativa/api/server.py +++ b/apps/normativa/api/server.py @@ -1,15 +1,14 @@ -import uuid import os +import uuid from contextlib import asynccontextmanager from pathlib import Path +from dotenv import load_dotenv # ← Agregar este import from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware -from fastapi.responses import StreamingResponse, FileResponse, RedirectResponse -from pydantic import BaseModel +from fastapi.responses import FileResponse, RedirectResponse, StreamingResponse from langfuse import Langfuse - -from dotenv import load_dotenv # ← Agregar este import +from pydantic import BaseModel from api import services from api.agent import Agent @@ -21,8 +20,8 @@ load_dotenv() # Configurar Langfuse desde variables de entorno langfuse = Langfuse( public_key=os.getenv("LANGFUSE_PUBLIC_KEY"), - secret_key=os.getenv("LANGFUSE_SECRET_KEY"), - host=os.getenv("LANGFUSE_HOST") + secret_key=os.getenv("LANGFUSE_SECRET_KEY"), + host=os.getenv("LANGFUSE_HOST"), ) @@ -34,12 +33,10 @@ PDF_PUBLIC_URLS = { "Disposiciones de carácter general aplicables a las sociedades controladoras de grupos financieros y subcontroladoras que regulan las materias que corresponden de manera conjunta a las Comisio.pdf": "https://www.cnbv.gob.mx/Normatividad/Disposiciones%20de%20car%C3%A1cter%20general%20aplicables%20a%20las%20sociedades%20controladoras%20de%20grupos%20financieros%20y%20subcontroladoras%20que%20regulan%20las%20materias%20que%20corresponden%20de%20manera%20conjunta%20a%20las%20Comisiones%20Nacionales%20Supervisoras.pdf", "Disposiciones de carácter general aplicables a los fondos de inversión y a las personas que les prestan servicios.pdf": "https://www.cnbv.gob.mx/Normatividad/Disposiciones%20de%20car%C3%A1cter%20general%20aplicables%20a%20los%20fondos%20de%20inversi%C3%B3n%20y%20a%20las%20personas%20que%20les%20prestan%20servicios.pdf", "Ley para la Transparencia y Ordenamiento de los Servicios Financieros.pdf": "https://www.cnbv.gob.mx/Normatividad/Ley%20para%20la%20Transparencia%20y%20Ordenamiento%20de%20los%20Servicios%20Financieros.pdf", - # Circulares CNBV adicionales "circular_servicios_de_inversion.pdf": "https://www.cnbv.gob.mx/Normatividad/Disposiciones%20de%20car%C3%A1cter%20general%20aplicables%20a%20las%20entidades%20financieras%20y%20dem%C3%A1s%20personas%20que%20proporcionen%20servicios%20de.pdf", "circular_unica_de_auditores_externos.pdf": "https://www.cnbv.gob.mx/Normatividad/Disposiciones%20de%20car%C3%A1cter%20general%20que%20establecen%20los%20requisitos%20que%20deber%C3%A1n%20cumplir%20los%20auditores%20y%20otros%20profesionales%20que.pdf", "ley_de_instituciones_de_Credito.pdf": "https://www.cnbv.gob.mx/Normatividad/Ley%20de%20Instituciones%20de%20Cr%C3%A9dito.pdf", - # Circulares de Banxico "circular_13_2007.pdf": "https://www.banxico.org.mx/marco-normativo/normativa-emitida-por-el-banco-de-mexico/circular-13-2007/cobro-intereses-por-adelantad.html", "circular_13_2011.pdf": "https://www.banxico.org.mx/marco-normativo/normativa-emitida-por-el-banco-de-mexico/circular-13-2011/%7BBA4CBC28-A468-16C9-6F17-9EA9D7B03318%7D.pdf", @@ -55,15 +52,12 @@ PDF_PUBLIC_URLS = { "circular_36_2010.pdf": "https://www.banxico.org.mx/marco-normativo/normativa-emitida-por-el-banco-de-mexico/circular-36-2010/%7B26C55DE6-CC3A-3368-34FC-1A6C50B11130%7D.pdf", "circular_3_2012.pdf": "https://www.banxico.org.mx/marco-normativo/normativa-emitida-por-el-banco-de-mexico/circular-3-2012/%7B4E0281A4-7AD8-1462-BC79-7F2925F3171D%7D.pdf", "circular_4_2012.pdf": "https://www.banxico.org.mx/marco-normativo/normativa-emitida-por-el-banco-de-mexico/circular-4-2012/%7B97C62974-1C94-19AE-AB5A-D0D949A36247%7D.pdf", - # CONDUSEF "circular_unica_de_condusef.pdf": "https://www.condusef.gob.mx/documentos/marco_legal/disposiciones-transparencia-if-sofom.pdf", "ley_para_regular_las_sociedades_de_informacion_crediticia.pdf": "https://www.condusef.gob.mx/documentos/marco_legal/disposiciones-transparencia-if-sofom.pdf", - # Leyes federales "ley_federal_de_proteccion_de_datos_personales_en_posesion_de_los_particulares.pdf": "https://www.diputados.gob.mx/LeyesBiblio/pdf/LFPDPPP.pdf", "reglamento_de_la_ley_federal_de_proteccion_de_datos_personales_en_posesion_de_los_particulares.pdf": "https://www.diputados.gob.mx/LeyesBiblio/regley/Reg_LFPDPPP.pdf", - # SharePoint Banorte "Modificaciones Recursos Procedencia Ilícita jul 25 PLD.pdf": "https://gfbanorte.sharepoint.com/:w:/r/sites/Formatosyplantillas/Documentos%20compartidos/Otros/Modificaciones%20Recursos%20Procedencia%20Il%C3%ADcita%20jul%2025%20PLD.docx?d=w6a941e9e2c26403ea41c12de35536516&csf=1&web=1&e=EHtc9b", } @@ -79,17 +73,17 @@ app = FastAPI(lifespan=lifespan) app.add_middleware( CORSMiddleware, - allow_origins=["*"], + allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], - expose_headers=["*"] + expose_headers=["*"], ) agent = Agent() PDF_FOLDER = Path(__file__).parent / "agent" / "pdf" -PDF_FOLDER.mkdir(parents=True, exist_ok=True) +PDF_FOLDER.mkdir(parents=True, exist_ok=True) @app.post("/api/v1/conversation") @@ -110,21 +104,21 @@ async def send(message: Message): trace = langfuse.trace( name="rag_chat", session_id=str(message.conversation_id), - input={"prompt": message.prompt} + input={"prompt": message.prompt}, ) - + def b64_sse(func): async def wrapper(*args, **kwargs): response_parts = [] - + async for chunk in func(*args, **kwargs): if chunk.type == "text" and chunk.content: response_parts.append(str(chunk.content)) - + content = chunk.model_dump_json() data = f"data: {content}\n\n" yield data - + # Solo registrar input y output full_response = "".join(response_parts) trace.update(output={"response": full_response}) @@ -139,17 +133,21 @@ async def send(message: Message): @app.get("/api/pdf/{filename}") async def get_pdf(filename: str): print(f"🔍 Solicitud PDF para: {filename}") - - if not filename.lower().endswith('.pdf'): + + if not filename.lower().endswith(".pdf"): print(f"❌ Archivo no es PDF: {filename}") raise HTTPException(status_code=400, detail="El archivo debe ser un PDF") - - if '..' in filename or ('/' in filename and not filename.startswith('http')) or '\\' in filename: + + if ( + ".." in filename + or ("/" in filename and not filename.startswith("http")) + or "\\" in filename + ): print(f"❌ Nombre de archivo inválido: {filename}") raise HTTPException(status_code=400, detail="Nombre de archivo inválido") - + public_url = PDF_PUBLIC_URLS.get(filename) - + if public_url: print(f"✅ Redirigiendo a URL pública: {public_url}") return RedirectResponse( @@ -159,39 +157,41 @@ async def get_pdf(filename: str): "Cache-Control": "public, max-age=3600", "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, OPTIONS", - "Access-Control-Allow-Headers": "*" - } + "Access-Control-Allow-Headers": "*", + }, ) - + pdf_path = PDF_FOLDER / filename - + if not pdf_path.exists(): print(f"❌ PDF no encontrado: {pdf_path}") - raise HTTPException(status_code=404, detail=f"PDF no encontrado. Archivo: {filename}") - + raise HTTPException( + status_code=404, detail=f"PDF no encontrado. Archivo: {filename}" + ) + if not pdf_path.is_file(): print(f"❌ No es un archivo: {pdf_path}") raise HTTPException(status_code=404, detail="El recurso no es un archivo") - + file_size = pdf_path.stat().st_size print(f"📄 Sirviendo archivo local: {filename} ({file_size} bytes)") - + if file_size == 0: print(f"❌ Archivo vacío: {pdf_path}") raise HTTPException(status_code=500, detail="El archivo PDF está vacío") - + return FileResponse( path=str(pdf_path), media_type="application/pdf", filename=filename, headers={ - "Content-Disposition": f"inline; filename={filename}", + "Content-Disposition": f"inline; filename={filename}", "Content-Type": "application/pdf", "Cache-Control": "public, max-age=3600", - "X-Frame-Options": "ALLOWALL", + "X-Frame-Options": "ALLOWALL", "X-Content-Type-Options": "nosniff", - "Access-Control-Allow-Origin": "*" - } + "Access-Control-Allow-Origin": "*", + }, ) @@ -199,29 +199,33 @@ async def get_pdf(filename: str): async def list_pdfs(): try: pdf_files = [] - + for filename, url in PDF_PUBLIC_URLS.items(): - pdf_files.append({ - "filename": filename, - "size": "N/A (Público)", - "url": f"/api/pdf/{filename}", - "public_url": url, - "type": "public" - }) - + pdf_files.append( + { + "filename": filename, + "size": "N/A (Público)", + "url": f"/api/pdf/{filename}", + "public_url": url, + "type": "public", + } + ) + local_files = [] for pattern in ["*.pdf", "*.PDF"]: for file_path in PDF_FOLDER.glob(pattern): if file_path.is_file() and file_path.name not in PDF_PUBLIC_URLS: - local_files.append({ - "filename": file_path.name, - "size": file_path.stat().st_size, - "url": f"/api/pdf/{file_path.name}", - "type": "local" - }) - + local_files.append( + { + "filename": file_path.name, + "size": file_path.stat().st_size, + "url": f"/api/pdf/{file_path.name}", + "type": "local", + } + ) + pdf_files.extend(local_files) - + debug_info = { "current_working_directory": str(Path.cwd()), "pdf_folder_path": str(PDF_FOLDER.absolute()), @@ -230,52 +234,49 @@ async def list_pdfs(): "local_files_count": len(local_files), "public_files": list(PDF_PUBLIC_URLS.keys()), } - - return { - "pdfs": pdf_files, - "debug": debug_info, - "total_pdfs": len(pdf_files) - } + + return {"pdfs": pdf_files, "debug": debug_info, "total_pdfs": len(pdf_files)} except Exception as e: import traceback + return { "error": str(e), "traceback": traceback.format_exc(), "debug": { "current_working_directory": str(Path.cwd()), - "script_file_path": __file__ if '__file__' in globals() else "unknown" - } + "script_file_path": __file__ if "__file__" in globals() else "unknown", + }, } @app.get("/api/pdf/{filename}/info") async def get_pdf_info(filename: str): - if not filename.lower().endswith('.pdf'): + if not filename.lower().endswith(".pdf"): raise HTTPException(status_code=400, detail="El archivo debe ser un PDF") - - if '..' in filename or '/' in filename or '\\' in filename: + + if ".." in filename or "/" in filename or "\\" in filename: raise HTTPException(status_code=400, detail="Nombre de archivo inválido") - + public_url = PDF_PUBLIC_URLS.get(filename) if public_url: return { "filename": filename, "size": "N/A", - "size_mb": "N/A", + "size_mb": "N/A", "modified": "N/A", "url": f"/api/pdf/{filename}", "public_url": public_url, - "type": "public" + "type": "public", } - + pdf_path = PDF_FOLDER / filename - + if not pdf_path.exists(): raise HTTPException(status_code=404, detail="PDF no encontrado") - + if not pdf_path.is_file(): raise HTTPException(status_code=404, detail="El recurso no es un archivo") - + try: file_stat = pdf_path.stat() return { @@ -284,10 +285,12 @@ async def get_pdf_info(filename: str): "size_mb": round(file_stat.st_size / (1024 * 1024), 2), "modified": file_stat.st_mtime, "url": f"/api/pdf/{filename}", - "type": "local" + "type": "local", } except Exception as e: - raise HTTPException(status_code=500, detail=f"Error al obtener información del PDF: {str(e)}") + raise HTTPException( + status_code=500, detail=f"Error al obtener información del PDF: {str(e)}" + ) @app.get("/api/health") @@ -296,5 +299,10 @@ async def health_check(): "status": "healthy", "pdf_folder": str(PDF_FOLDER), "pdf_folder_exists": PDF_FOLDER.exists(), - "public_urls_configured": len(PDF_PUBLIC_URLS) - } \ No newline at end of file + "public_urls_configured": len(PDF_PUBLIC_URLS), + } + + +@app.get("/") +async def health(): + return {"status": "ok"} diff --git a/apps/pyme/.k8s/deployment.yaml b/apps/pyme/.k8s/deployment.yaml new file mode 100644 index 0000000..5b6623b --- /dev/null +++ b/apps/pyme/.k8s/deployment.yaml @@ -0,0 +1,42 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mayacontigo-pyme + namespace: apps + labels: + app: mayacontigo-pyme +spec: + replicas: 1 + selector: + matchLabels: + app: mayacontigo-pyme + template: + metadata: + labels: + app: mayacontigo-pyme + spec: + imagePullSecrets: + - name: gitea-registry-cred + containers: + - name: mayacontigo-pyme + image: gitea.ia-innovacion.work/innovacion/mayacontigo-pyme:latest + env: + - name: VAULT_TOKEN + valueFrom: + secretKeyRef: + name: mayacontigo-pyme-secret + key: VAULT_TOKEN + ports: + - containerPort: 80 + readinessProbe: + httpGet: + path: / + port: 80 + initialDelaySeconds: 5 + periodSeconds: 10 + livenessProbe: + httpGet: + path: / + port: 80 + initialDelaySeconds: 15 + periodSeconds: 20 diff --git a/apps/pyme/.k8s/ingress.yaml b/apps/pyme/.k8s/ingress.yaml new file mode 100644 index 0000000..b860f7d --- /dev/null +++ b/apps/pyme/.k8s/ingress.yaml @@ -0,0 +1,19 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: mayacontigo-pyme-ingress + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + ingressClassName: nginx + rules: + - host: mayacontigo-pyme.app.ia-innovacion.work + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: mayacontigo-pyme-service + port: + number: 80 diff --git a/apps/pyme/.k8s/secrets.yaml b/apps/pyme/.k8s/secrets.yaml new file mode 100644 index 0000000..a203882 --- /dev/null +++ b/apps/pyme/.k8s/secrets.yaml @@ -0,0 +1,17 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: mayacontigo-pyme-vault + namespace: apps +spec: + refreshInterval: "15s" + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + target: + name: mayacontigo-pyme-secret + data: + - secretKey: VAULT_TOKEN + remoteRef: + key: mayacontigo-pyme + property: VAULT_TOKEN diff --git a/apps/pyme/.k8s/service.yaml b/apps/pyme/.k8s/service.yaml new file mode 100644 index 0000000..462c173 --- /dev/null +++ b/apps/pyme/.k8s/service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: mayacontigo-pyme-service + labels: + app: mayacontigo-pyme +spec: + selector: + app: mayacontigo-pyme + ports: + - port: 80 + targetPort: 80 + protocol: TCP + type: ClusterIP diff --git a/apps/pyme/api/server.py b/apps/pyme/api/server.py index ab0bfcd..2afa44e 100644 --- a/apps/pyme/api/server.py +++ b/apps/pyme/api/server.py @@ -1,15 +1,14 @@ -import uuid import os +import uuid from contextlib import asynccontextmanager from pathlib import Path +from dotenv import load_dotenv # ← Agregar este import from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware -from fastapi.responses import StreamingResponse, FileResponse, RedirectResponse -from pydantic import BaseModel +from fastapi.responses import FileResponse, RedirectResponse, StreamingResponse from langfuse import Langfuse - -from dotenv import load_dotenv # ← Agregar este import +from pydantic import BaseModel from api import services from api.agent import Agent @@ -21,14 +20,13 @@ load_dotenv() # Configurar Langfuse desde variables de entorno langfuse = Langfuse( public_key=os.getenv("LANGFUSE_PUBLIC_KEY"), - secret_key=os.getenv("LANGFUSE_SECRET_KEY"), - host=os.getenv("LANGFUSE_HOST") + secret_key=os.getenv("LANGFUSE_SECRET_KEY"), + host=os.getenv("LANGFUSE_HOST"), ) # Mapeo completo de archivos a URLs públicas -PDF_PUBLIC_URLS = { - } +PDF_PUBLIC_URLS = {} @asynccontextmanager @@ -41,17 +39,17 @@ app = FastAPI(lifespan=lifespan) app.add_middleware( CORSMiddleware, - allow_origins=["*"], + allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], - expose_headers=["*"] + expose_headers=["*"], ) agent = Agent() PDF_FOLDER = Path(__file__).parent / "agent" / "pdf" -PDF_FOLDER.mkdir(parents=True, exist_ok=True) +PDF_FOLDER.mkdir(parents=True, exist_ok=True) @app.post("/api/v1/conversation") @@ -72,21 +70,21 @@ async def send(message: Message): trace = langfuse.trace( name="rag_chat", session_id=str(message.conversation_id), - input={"prompt": message.prompt} + input={"prompt": message.prompt}, ) - + def b64_sse(func): async def wrapper(*args, **kwargs): response_parts = [] - + async for chunk in func(*args, **kwargs): if chunk.type == "text" and chunk.content: response_parts.append(str(chunk.content)) - + content = chunk.model_dump_json() data = f"data: {content}\n\n" yield data - + # Solo registrar input y output full_response = "".join(response_parts) trace.update(output={"response": full_response}) @@ -101,17 +99,21 @@ async def send(message: Message): @app.get("/api/pdf/{filename}") async def get_pdf(filename: str): print(f"🔍 Solicitud PDF para: {filename}") - - if not filename.lower().endswith('.pdf'): + + if not filename.lower().endswith(".pdf"): print(f"❌ Archivo no es PDF: {filename}") raise HTTPException(status_code=400, detail="El archivo debe ser un PDF") - - if '..' in filename or ('/' in filename and not filename.startswith('http')) or '\\' in filename: + + if ( + ".." in filename + or ("/" in filename and not filename.startswith("http")) + or "\\" in filename + ): print(f"❌ Nombre de archivo inválido: {filename}") raise HTTPException(status_code=400, detail="Nombre de archivo inválido") - + public_url = PDF_PUBLIC_URLS.get(filename) - + if public_url: print(f"✅ Redirigiendo a URL pública: {public_url}") return RedirectResponse( @@ -121,39 +123,41 @@ async def get_pdf(filename: str): "Cache-Control": "public, max-age=3600", "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, OPTIONS", - "Access-Control-Allow-Headers": "*" - } + "Access-Control-Allow-Headers": "*", + }, ) - + pdf_path = PDF_FOLDER / filename - + if not pdf_path.exists(): print(f"❌ PDF no encontrado: {pdf_path}") - raise HTTPException(status_code=404, detail=f"PDF no encontrado. Archivo: {filename}") - + raise HTTPException( + status_code=404, detail=f"PDF no encontrado. Archivo: {filename}" + ) + if not pdf_path.is_file(): print(f"❌ No es un archivo: {pdf_path}") raise HTTPException(status_code=404, detail="El recurso no es un archivo") - + file_size = pdf_path.stat().st_size print(f"📄 Sirviendo archivo local: {filename} ({file_size} bytes)") - + if file_size == 0: print(f"❌ Archivo vacío: {pdf_path}") raise HTTPException(status_code=500, detail="El archivo PDF está vacío") - + return FileResponse( path=str(pdf_path), media_type="application/pdf", filename=filename, headers={ - "Content-Disposition": f"inline; filename={filename}", + "Content-Disposition": f"inline; filename={filename}", "Content-Type": "application/pdf", "Cache-Control": "public, max-age=3600", - "X-Frame-Options": "ALLOWALL", + "X-Frame-Options": "ALLOWALL", "X-Content-Type-Options": "nosniff", - "Access-Control-Allow-Origin": "*" - } + "Access-Control-Allow-Origin": "*", + }, ) @@ -161,29 +165,33 @@ async def get_pdf(filename: str): async def list_pdfs(): try: pdf_files = [] - + for filename, url in PDF_PUBLIC_URLS.items(): - pdf_files.append({ - "filename": filename, - "size": "N/A (Público)", - "url": f"/api/pdf/{filename}", - "public_url": url, - "type": "public" - }) - + pdf_files.append( + { + "filename": filename, + "size": "N/A (Público)", + "url": f"/api/pdf/{filename}", + "public_url": url, + "type": "public", + } + ) + local_files = [] for pattern in ["*.pdf", "*.PDF"]: for file_path in PDF_FOLDER.glob(pattern): if file_path.is_file() and file_path.name not in PDF_PUBLIC_URLS: - local_files.append({ - "filename": file_path.name, - "size": file_path.stat().st_size, - "url": f"/api/pdf/{file_path.name}", - "type": "local" - }) - + local_files.append( + { + "filename": file_path.name, + "size": file_path.stat().st_size, + "url": f"/api/pdf/{file_path.name}", + "type": "local", + } + ) + pdf_files.extend(local_files) - + debug_info = { "current_working_directory": str(Path.cwd()), "pdf_folder_path": str(PDF_FOLDER.absolute()), @@ -192,52 +200,49 @@ async def list_pdfs(): "local_files_count": len(local_files), "public_files": list(PDF_PUBLIC_URLS.keys()), } - - return { - "pdfs": pdf_files, - "debug": debug_info, - "total_pdfs": len(pdf_files) - } + + return {"pdfs": pdf_files, "debug": debug_info, "total_pdfs": len(pdf_files)} except Exception as e: import traceback + return { "error": str(e), "traceback": traceback.format_exc(), "debug": { "current_working_directory": str(Path.cwd()), - "script_file_path": __file__ if '__file__' in globals() else "unknown" - } + "script_file_path": __file__ if "__file__" in globals() else "unknown", + }, } @app.get("/api/pdf/{filename}/info") async def get_pdf_info(filename: str): - if not filename.lower().endswith('.pdf'): + if not filename.lower().endswith(".pdf"): raise HTTPException(status_code=400, detail="El archivo debe ser un PDF") - - if '..' in filename or '/' in filename or '\\' in filename: + + if ".." in filename or "/" in filename or "\\" in filename: raise HTTPException(status_code=400, detail="Nombre de archivo inválido") - + public_url = PDF_PUBLIC_URLS.get(filename) if public_url: return { "filename": filename, "size": "N/A", - "size_mb": "N/A", + "size_mb": "N/A", "modified": "N/A", "url": f"/api/pdf/{filename}", "public_url": public_url, - "type": "public" + "type": "public", } - + pdf_path = PDF_FOLDER / filename - + if not pdf_path.exists(): raise HTTPException(status_code=404, detail="PDF no encontrado") - + if not pdf_path.is_file(): raise HTTPException(status_code=404, detail="El recurso no es un archivo") - + try: file_stat = pdf_path.stat() return { @@ -246,10 +251,12 @@ async def get_pdf_info(filename: str): "size_mb": round(file_stat.st_size / (1024 * 1024), 2), "modified": file_stat.st_mtime, "url": f"/api/pdf/{filename}", - "type": "local" + "type": "local", } except Exception as e: - raise HTTPException(status_code=500, detail=f"Error al obtener información del PDF: {str(e)}") + raise HTTPException( + status_code=500, detail=f"Error al obtener información del PDF: {str(e)}" + ) @app.get("/api/health") @@ -258,5 +265,10 @@ async def health_check(): "status": "healthy", "pdf_folder": str(PDF_FOLDER), "pdf_folder_exists": PDF_FOLDER.exists(), - "public_urls_configured": len(PDF_PUBLIC_URLS) - } \ No newline at end of file + "public_urls_configured": len(PDF_PUBLIC_URLS), + } + + +@app.get("/") +async def health(): + return {"app": "RAG PyME", "status": "OK"} diff --git a/apps/riesgos/.k8s/deployment.yaml b/apps/riesgos/.k8s/deployment.yaml new file mode 100644 index 0000000..373379b --- /dev/null +++ b/apps/riesgos/.k8s/deployment.yaml @@ -0,0 +1,42 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mayacontigo-riesgos + namespace: apps + labels: + app: mayacontigo-riesgos +spec: + replicas: 1 + selector: + matchLabels: + app: mayacontigo-riesgos + template: + metadata: + labels: + app: mayacontigo-riesgos + spec: + imagePullSecrets: + - name: gitea-registry-cred + containers: + - name: mayacontigo-riesgos + image: gitea.ia-innovacion.work/innovacion/mayacontigo-riesgos:latest + env: + - name: VAULT_TOKEN + valueFrom: + secretKeyRef: + name: mayacontigo-riesgos-secret + key: VAULT_TOKEN + ports: + - containerPort: 80 + readinessProbe: + httpGet: + path: / + port: 80 + initialDelaySeconds: 5 + periodSeconds: 10 + livenessProbe: + httpGet: + path: / + port: 80 + initialDelaySeconds: 15 + periodSeconds: 20 diff --git a/apps/riesgos/.k8s/ingress.yaml b/apps/riesgos/.k8s/ingress.yaml new file mode 100644 index 0000000..5169220 --- /dev/null +++ b/apps/riesgos/.k8s/ingress.yaml @@ -0,0 +1,19 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: mayacontigo-riesgos-ingress + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + ingressClassName: nginx + rules: + - host: mayacontigo-riesgos.app.ia-innovacion.work + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: mayacontigo-riesgos-service + port: + number: 80 diff --git a/apps/riesgos/.k8s/secrets.yaml b/apps/riesgos/.k8s/secrets.yaml new file mode 100644 index 0000000..639e4db --- /dev/null +++ b/apps/riesgos/.k8s/secrets.yaml @@ -0,0 +1,17 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: mayacontigo-riesgos-vault + namespace: apps +spec: + refreshInterval: "15s" + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + target: + name: mayacontigo-riesgos-secret + data: + - secretKey: VAULT_TOKEN + remoteRef: + key: mayacontigo-riesgos + property: VAULT_TOKEN diff --git a/apps/riesgos/.k8s/service.yaml b/apps/riesgos/.k8s/service.yaml new file mode 100644 index 0000000..715bb7e --- /dev/null +++ b/apps/riesgos/.k8s/service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: mayacontigo-riesgos-service + labels: + app: mayacontigo-riesgos +spec: + selector: + app: mayacontigo-riesgos + ports: + - port: 80 + targetPort: 80 + protocol: TCP + type: ClusterIP diff --git a/apps/riesgos/api/server/__init__.py b/apps/riesgos/api/server/__init__.py index f496973..8956316 100644 --- a/apps/riesgos/api/server/__init__.py +++ b/apps/riesgos/api/server/__init__.py @@ -20,4 +20,10 @@ app = FastAPI( openapi_url="/api/openapi.json", ) + +@app.get("/") +async def health(): + return {"status": "ok"} + + app.include_router(router) diff --git a/apps/voz-del-cliente/.k8s/deployment.yaml b/apps/voz-del-cliente/.k8s/deployment.yaml new file mode 100644 index 0000000..c8aefa7 --- /dev/null +++ b/apps/voz-del-cliente/.k8s/deployment.yaml @@ -0,0 +1,42 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mayacontigo-voz-del-cliente + namespace: apps + labels: + app: mayacontigo-voz-del-cliente +spec: + replicas: 1 + selector: + matchLabels: + app: mayacontigo-voz-del-cliente + template: + metadata: + labels: + app: mayacontigo-voz-del-cliente + spec: + imagePullSecrets: + - name: gitea-registry-cred + containers: + - name: mayacontigo-voz-del-cliente + image: gitea.ia-innovacion.work/innovacion/mayacontigo-voz-del-cliente:latest + env: + - name: VAULT_TOKEN + valueFrom: + secretKeyRef: + name: mayacontigo-voz-del-cliente-secret + key: VAULT_TOKEN + ports: + - containerPort: 80 + readinessProbe: + httpGet: + path: / + port: 80 + initialDelaySeconds: 5 + periodSeconds: 10 + livenessProbe: + httpGet: + path: / + port: 80 + initialDelaySeconds: 15 + periodSeconds: 20 diff --git a/apps/voz-del-cliente/.k8s/ingress.yaml b/apps/voz-del-cliente/.k8s/ingress.yaml new file mode 100644 index 0000000..6953bea --- /dev/null +++ b/apps/voz-del-cliente/.k8s/ingress.yaml @@ -0,0 +1,19 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: mayacontigo-voz-del-cliente-ingress + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + ingressClassName: nginx + rules: + - host: mayacontigo-voz-del-cliente.app.ia-innovacion.work + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: mayacontigo-voz-del-cliente-service + port: + number: 80 diff --git a/apps/voz-del-cliente/.k8s/secrets.yaml b/apps/voz-del-cliente/.k8s/secrets.yaml new file mode 100644 index 0000000..2f28a92 --- /dev/null +++ b/apps/voz-del-cliente/.k8s/secrets.yaml @@ -0,0 +1,17 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: mayacontigo-voz-del-cliente-vault + namespace: apps +spec: + refreshInterval: "15s" + secretStoreRef: + name: vault-backend + kind: ClusterSecretStore + target: + name: mayacontigo-voz-del-cliente-secret + data: + - secretKey: VAULT_TOKEN + remoteRef: + key: mayacontigo-voz-del-cliente + property: VAULT_TOKEN diff --git a/apps/voz-del-cliente/.k8s/service.yaml b/apps/voz-del-cliente/.k8s/service.yaml new file mode 100644 index 0000000..b97a689 --- /dev/null +++ b/apps/voz-del-cliente/.k8s/service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: mayacontigo-voz-del-cliente-service + labels: + app: mayacontigo-voz-del-cliente +spec: + selector: + app: mayacontigo-voz-del-cliente + ports: + - port: 80 + targetPort: 80 + protocol: TCP + type: ClusterIP diff --git a/apps/voz-del-cliente/api/config.py b/apps/voz-del-cliente/api/config.py index 2f9057e..e50504b 100644 --- a/apps/voz-del-cliente/api/config.py +++ b/apps/voz-del-cliente/api/config.py @@ -1,9 +1,7 @@ from hvac import Client from pydantic import Field -from dotenv import load_dotenv from pydantic_settings import BaseSettings - client = Client(url="https://vault.ia-innovacion.work") if not client.is_authenticated(): @@ -14,6 +12,7 @@ secret_map = client.secrets.kv.v2.read_secret_version( )["data"]["data"] class Settings(BaseSettings): + """ Esta clase obtiene sus valores de variables de ambiente. Si no estan en el ambiente, los jala de nuestra Vault. @@ -39,11 +38,10 @@ class Settings(BaseSettings): async def init_mongo_db(self): """Este helper inicia la conexion enter el MongoDB ORM y nuestra instancia""" + from banortegpt.database.mongo_memory.models import Conversation from beanie import init_beanie from motor.motor_asyncio import AsyncIOMotorClient - from banortegpt.database.mongo_memory.models import Conversation - await init_beanie( database=AsyncIOMotorClient(self.mongodb_url).voz_del_cliente, document_models=[Conversation], diff --git a/apps/voz-del-cliente/api/server.py b/apps/voz-del-cliente/api/server.py index 471b410..2273935 100644 --- a/apps/voz-del-cliente/api/server.py +++ b/apps/voz-del-cliente/api/server.py @@ -35,16 +35,24 @@ class Message(BaseModel): @app.post("/api/v1/message") async def send(message: Message): - def b64_sse(func): """Este helper transforma un generador de strings a un generador del protocolo SSE""" + async def wrapper(*args, **kwargs): async for chunk in func(*args, **kwargs): content = chunk.model_dump_json() data = f"data: {content}\n\n" yield data + return wrapper sse_stream = b64_sse(services.stream) - generator = sse_stream(agent, message.prompt, message.conversation_id, message.with_deep_research) + generator = sse_stream( + agent, message.prompt, message.conversation_id, message.with_deep_research + ) return StreamingResponse(generator, media_type="text/event-stream") + + +@app.get("/") +async def health(): + return {"app": "Voz del Cliente", "status": "OK"} diff --git a/compose.yaml b/compose.yaml index 5f6cdd3..7e3fea9 100644 --- a/compose.yaml +++ b/compose.yaml @@ -45,16 +45,16 @@ services: - traefik.http.routers.ocp.entrypoints=web - traefik.http.routers.ocp.middlewares=ocp-strip - traefik.http.middlewares.ocp-strip.stripprefix.prefixes=/api/mayaocp - ChatEgresos: - image: mayacontigo/chategresos:latest + egresos: + image: mayacontigo/egresos:latest build: context: . dockerfile: .containers/unit/Dockerfile args: - PACKAGE: ChatEgresos + PACKAGE: egresos x-bake: tags: - - mayacontigo/chategresos:latest + - mayacontigo/egresos:latest ports: - 8001:80 labels: @@ -69,7 +69,7 @@ services: context: . dockerfile: .containers/unit/Dockerfile args: - PACKAGE: nnormativa + PACKAGE: voz-del-cliente x-bake: tags: - mayacontigo/nnormativa:latest @@ -99,42 +99,6 @@ services: - traefik.http.routers.normativa.entrypoints=web - traefik.http.routers.normativa.middlewares=normativa-strip - traefik.http.middlewares.normativa-strip.stripprefix.prefixes=/api/mayanormativa - Test: - image: mayacontigo/Test:latest - build: - context: . - dockerfile: .containers/unit/Dockerfile - args: - PACKAGE: Test - x-bake: - tags: - - mayacontigo/Test:latest - ports: - - 8002:80 - labels: - - traefik.enable=true - - traefik.http.routers.Test.rule=PathPrefix(`/api/mayaTest`) - - traefik.http.routers.Test.entrypoints=web - - traefik.http.routers.Test.middlewares=Test-strip - - traefik.http.middlewares.Test-strip.stripprefix.prefixes=/api/mayaTest - nnormativa: - image: mayacontigo/nnormativa:latest - build: - context: . - dockerfile: .containers/unit/Dockerfile - args: - PACKAGE: nnormativa - x-bake: - tags: - - mayacontigo/nnormativa:latest - ports: - - 8003:80 - labels: - - traefik.enable=true - - traefik.http.routers.nnormativa.rule=PathPrefix(`/api/mayannormativa`) - - traefik.http.routers.nnormativa.entrypoints=web - - traefik.http.routers.nnormativa.middlewares=nnormativa-strip - - traefik.http.middlewares.nnormativa-strip.stripprefix.prefixes=/api/mayannormativa pyme: image: mayacontigo/pyme:latest build: diff --git a/scripts/replace-app-name.sh b/scripts/replace-app-name.sh new file mode 100755 index 0000000..0b29b94 --- /dev/null +++ b/scripts/replace-app-name.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# Replace app name in Kubernetes manifest files +# Usage: ./replace-app-name.sh +# Example: ./replace-app-name.sh bursatil inversionistas + +if [ $# -ne 2 ]; then + echo "Usage: $0 " + echo "Example: $0 bursatil inversionistas" + exit 1 +fi + +APP_NAME=$1 +OLD_NAME=$2 +K8S_DIR="apps/$APP_NAME/.k8s" + +if [ ! -d "$K8S_DIR" ]; then + echo "Error: Directory $K8S_DIR does not exist" + exit 1 +fi + +echo "Replacing '$OLD_NAME' with '$APP_NAME' in $K8S_DIR" + +for file in "$K8S_DIR"/*.yaml; do + if [ -f "$file" ]; then + echo " Processing: $(basename $file)" + sed -i "s/$OLD_NAME/$APP_NAME/g" "$file" + fi +done + +echo "Done!" diff --git a/uv.lock b/uv.lock index 1790cf6..4cb5e2f 100644 --- a/uv.lock +++ b/uv.lock @@ -13,8 +13,8 @@ members = [ "azure-storage", "banortegpt", "bursatil", - "chategresos", "chunk-with-llm", + "egresos", "google-storage", "inversionistas", "mongo-memory", @@ -27,7 +27,6 @@ members = [ "riesgos", "search-evaluator", "synthetic-question-generator", - "test", "vector-db-migrator", "vertex-ai-gemini", "voz-del-cliente", @@ -503,33 +502,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, ] -[[package]] -name = "chategresos" -version = "0.1.0" -source = { virtual = "apps/ChatEgresos" } -dependencies = [ - { name = "aiohttp" }, - { name = "fastapi" }, - { name = "hvac" }, - { name = "langchain-azure-ai", extra = ["opentelemetry"] }, - { name = "mongo-memory" }, - { name = "pydantic-settings" }, - { name = "qdrant" }, - { name = "uvicorn" }, -] - -[package.metadata] -requires-dist = [ - { name = "aiohttp", specifier = ">=3.11.16" }, - { name = "fastapi", specifier = ">=0.115.6" }, - { name = "hvac", specifier = ">=2.3.0" }, - { name = "langchain-azure-ai", extras = ["opentelemetry"], specifier = ">=0.1.4" }, - { name = "mongo-memory", editable = "packages/mongo-memory" }, - { name = "pydantic-settings", specifier = ">=2.8.1" }, - { name = "qdrant", editable = "packages/qdrant" }, - { name = "uvicorn", specifier = ">=0.34.0" }, -] - [[package]] name = "chunk-with-llm" version = "0.1.0" @@ -749,6 +721,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408, upload-time = "2024-04-23T18:57:14.835Z" }, ] +[[package]] +name = "egresos" +version = "0.1.0" +source = { virtual = "apps/egresos" } +dependencies = [ + { name = "aiohttp" }, + { name = "fastapi" }, + { name = "hvac" }, + { name = "langchain-azure-ai", extra = ["opentelemetry"] }, + { name = "mongo-memory" }, + { name = "pydantic-settings" }, + { name = "qdrant" }, + { name = "uvicorn" }, +] + +[package.metadata] +requires-dist = [ + { name = "aiohttp", specifier = ">=3.11.16" }, + { name = "fastapi", specifier = ">=0.115.6" }, + { name = "hvac", specifier = ">=2.3.0" }, + { name = "langchain-azure-ai", extras = ["opentelemetry"], specifier = ">=0.1.4" }, + { name = "mongo-memory", editable = "packages/mongo-memory" }, + { name = "pydantic-settings", specifier = ">=2.8.1" }, + { name = "qdrant", editable = "packages/qdrant" }, + { name = "uvicorn", specifier = ">=0.34.0" }, +] + [[package]] name = "email-validator" version = "2.2.0" @@ -3438,31 +3437,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248, upload-time = "2025-04-02T08:25:07.678Z" }, ] -[[package]] -name = "test" -version = "0.1.0" -source = { virtual = "apps/Test" } -dependencies = [ - { name = "aiohttp" }, - { name = "fastapi" }, - { name = "hvac" }, - { name = "langchain-azure-ai", extra = ["opentelemetry"] }, - { name = "mongo-memory" }, - { name = "pydantic-settings" }, - { name = "uvicorn" }, -] - -[package.metadata] -requires-dist = [ - { name = "aiohttp", specifier = ">=3.11.16" }, - { name = "fastapi", specifier = ">=0.115.6" }, - { name = "hvac", specifier = ">=2.3.0" }, - { name = "langchain-azure-ai", extras = ["opentelemetry"], specifier = ">=0.1.4" }, - { name = "mongo-memory", editable = "packages/mongo-memory" }, - { name = "pydantic-settings", specifier = ">=2.8.1" }, - { name = "uvicorn", specifier = ">=0.34.0" }, -] - [[package]] name = "tiktoken" version = "0.9.0"