Artículo Alberto Fuentes · oct 2, 2025 2m read

Hola,

He pensado en compartir con vosotros un hook bastante útil que me ha ayudado cuando desarrollo en Health Connect Cloud con VS Code y GitBash. Al desarrollar en Health Connect Cloud, si hacéis cambios directamente en el servidor, como reglas de enrutamiento o despliegue de componentes, estos no se incluyen automáticamente en el control de versiones; por lo tanto, debéis exportarlos desde el servidor a vuestros archivos locales y hacer push a vuestro repositorio remoto. Estoy seguro de que hay métodos más sencillos para gestionar esto, que estoy en proceso de probar, pero como solución rápida pensé que sería útil tener un hook de pre-commit que active un recordatorio en GitBash. Mirad el ejemplo a continuación.

0
0 18
Anuncio Alberto Fuentes · sep 30, 2025

¿Te gustaría integrar capacidades de machine learning directamente en tu plataforma IRIS desde tus datos SQL?

<iframe width="560" height="315" src="https://www.youtube.com/embed/kBl-WrgijBA?si=oEbita-dqLYfSrXn" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

Este vídeo te muestra exactamente cómo hacerlo utilizando InterSystems IRIS e IntegratedML.

En el vídeo 🎥 aprenderás:

  • Los conceptos básicos de IntegratedML: qué es y para qué sirve.
  • Cómo usarlo dentro de IRIS para crear, entrenar, evaluar y utilizar modelos de machine learning.
  • Ejemplos práctico paso a paso de esta funcionalidad integrada.

¿Por qué verlo?

  • Convierte a IRIS en una plataforma "todo en uno" donde crear rápidamente modelos de predicción desde SQL.
  • Facilita la adopción de IA en tus aplicaciones sin ser un experto en ciencia de datos.

👉 Te invito a verlo y comentarlo: comparte tus dudas, experiencias o ideas en la Comunidad.

0
0 15
Artículo Alberto Fuentes · sep 12, 2025 5m read

En el artículo anterior vimos cómo construir un agente de IA de atención al cliente con smolagents e InterSystems IRIS, combinando SQL, RAG con búsquedas vectoriales e interoperabilidad.

En ese caso, utilizamos modelos en la nube (OpenAI) para el LLM y los embeddings.

En esta ocasión daremos un paso más: ejecutar el mismo agente, pero con modelos locales gracias a Ollama.


¿Por qué ejecutar modelos en local?

El uso de LLMs en la nube es la opción más sencilla para empezar:

  • ✅ Modelos ya optimizados y mantenidos
  • ✅ Fácil acceso con una simple API
  • ✅ Servicio serverless: no necesitas preocuparte por el hardware ni mantenimiento
  • ❌ Costes por uso
  • ❌ Dependencia de servicios externos
  • ❌ Restricciones de privacidad al enviar datos

En cambio, al correr modelos en local conseguimos:

  • ✅ Control total sobre los datos y el entorno
  • ✅ Sin costes variables por uso
  • ✅ Posibilidad de hacer fine tuning o adaptar modelos con técnicas como LoRA (Low-Rank Adaptation), que permite entrenar algunas capas del modelo para ajustarlo a tu dominio concreto sin necesidad de reentrenarlo entero
  • ❌ Mayor consumo de recursos en tu servidor
  • ❌ Limitaciones en tamaño/modelos dependiendo del hardware

Ahí es donde entra en juego Ollama.


¿Qué es Ollama?

Ollama es una herramienta que facilita la ejecución de modelos de lenguaje y embeddings en tu propio ordenador con una experiencia muy simple:

  • Descargas modelos con un ollama pull
  • Los ejecutas de forma local, expuestos como una API HTTP
  • Los puedes integrar directamente en tus aplicaciones, igual que harías con OpenAI

En resumen: la misma API que usarías en la nube, pero corriendo en tu portátil o servidor.


Configuración básica de Ollama

Primero, instala Ollama desde su web y verifica que funciona:

ollama --version

Después, descarga un par de modelos:

# Descargar un modelo de embeddings
ollama pull nomic-embed-text:latest

# Descargar un modelo de lenguaje
ollama pull llama3.1:8b

# Ver todos los modelos disponibles
ollama list

Puedes probar embeddings directamente con un curl:

curl http://localhost:11434/api/embeddings -d '{
  "model": "nomic-embed-text:latest",
  "prompt": "Ollama hace sencillo ejecutar LLMs en local."
}'

Usar Ollama en el agente de IRIS

El repositorio Customer Support Agent Demo incluye ya la configuración para Ollama. Sólo necesitas:

  1. Descargar los modelos necesarios para ejecutarlos en Ollama Yo he utilizado nomic-embed-text para los embeddings de las búsquedas vectoriales y devstral como LLM.

  2. Configurar IRIS para utilizar embeddings de Ollama con el modelo local:

INSERT INTO %Embedding.Config (Name, Configuration, EmbeddingClass, VectorLength, Description)
  VALUES ('ollama-nomic-config', 
          '{"apiBase":"http://host.docker.internal:11434/api/embeddings", 
            "modelName": "nomic-embed-text:latest"}',
          'Embedding.Ollama', 
          768,  
          'embedding model in Ollama');
  1. Ajustar el tamaño de las columnas para almacenar vectores en las tablas del ejemplo (el modelo local tiene un tamaño diferente de vectores que el de OpenAI original).
ALTER TABLE Agent_Data.Products DROP COLUMN Embedding;
ALTER TABLE Agent_Data.Products ADD COLUMN Embedding VECTOR(FLOAT, 768);

ALTER TABLE Agent_Data.DocChunks DROP COLUMN Embedding;
ALTER TABLE Agent_Data.DocChunks ADD COLUMN Embedding VECTOR(FLOAT, 768);
  1. Configurar el fichero de entorno .env para indicar los modelos que queremos utilizar:
OPENAI_MODEL=devstral:24b-small-2505-q4_K_M
OPENAI_API_BASE=http://localhost:11434/v1
EMBEDDING_CONFIG_NAME=ollama-nomic-config
  1. Actualizar los embeddings

Como tenemos un modelo de embedding diferente del original, debemos actualizar los embeddings utilizando el modelo local nomic-embed-text

python scripts/embed_sql.py
  1. Ejecutar el agente para que haga uso de la nueva configuración

El código ya utilizará la configuración para que tanto los embeddings como el LLM se sirvan desde el endpoint local.

Con esta configuración podrás hacer preguntas como:

  • “¿Dónde está mi pedido #1001?”
  • “¿Cuál es el plazo de devolución?"

Y el agente utilizará:

  • SQL de IRIS para datos estructurados
  • Búsquedas vectoriales con embeddings de Ollama (local)
  • Interoperabilidad para simular llamadas a APIs externas
  • Un LLM local para planificar y generar código que llame a las herramientas necesarias para obtener la respuesta

Conclusión

Gracias a Ollama, podemos ejecutar nuestro Agente de Atención al Cliente con IRIS sin depender de la nube:

  • Privacidad y control de los datos
  • Coste cero por token
  • Flexibilidad total para probar y adaptar modelos (LoRA)

¿El reto? Necesitas una máquina con suficiente memoria y CPU/GPU para correr modelos grandes. Pero para prototipos y pruebas, es una opción muy potente y práctica.


Referencias útiles

0
0 29
Artículo Alberto Fuentes · sep 1, 2025 7m read

Las preguntas de atención al cliente pueden abarcar datos estructurados (pedidos, productos 🗃️), conocimiento no estructurado (docs/FAQs 📚) y otros sistemas integrados (actualizaciones de envío 🚚). En este post vamos a construir un agente de IA compacto que cubre los tres—usando:

  • 🧠 Python + smolagents para orquestar el “cerebro” del agente
  • 🧰 InterSystems IRIS para SQL, Búsqueda Semántica (RAG) e Interoperabilidad (una API de seguimiento de envío simulada)

⚡ TL;DR (versión express)

  • Construye un Agente de Atención al Cliente funcional con Python + smolagents orquestando herramientas sobre InterSystems IRIS (SQL, Búsqueda Semántica/RAG, Interoperabilidad con una API simulada).
  • Responde preguntas reales (p. ej., “¿Se entregó el pedido #1001?”“¿Cuál es el plazo de devolución?”) combinando tablas, documentos y llamadas de interoperabilidad.
  • Inicia IRIS en Docker, carga el esquema y datos de ejemplo, prepara los embeddings de los documentos para RAG, registra herramientas (SQL/RAG/API) y ejecuta el agente por línea de comando o interfaz gráfica.

image


🧭 Lo que vas a construir

Un Agente de Atención al Cliente capaz de:

  • 🔎 Consultar datos estructurados (clientes, pedidos, productos, envíos) vía SQL
  • 📚 Recuperar conocimiento no estructurado (FAQs y documentos) con RAG sobre IRIS Vector Search (Búsqueda Vectorial)
  • 🔌 Llamar a una API de seguimiento envío (simulada) mediante el framework de Interoperabilidad de IRIS, con Visual Trace para inspeccionar cada llamada

Arquitectura (resumida)

Usuario ➜ Agente (smolagents CodeAgent)
               ├─ Herramienta SQL ➜ Tablas en IRIS
               ├─ Herramienta RAG ➜ Búsqueda Vectorial en IRIS (embeddings + chunks)
               └─ Herramienta Seguimiento Envío ➜ Interoperabilidad IRIS (simulada) ➜ Visual Trace

¿Conoces smolagents? Es un framework ligero de agentes de Hugging Face en el que el modelo planifica y utiliza tus herramientas; como alternativas, puedes considerar LangGraph y LlamaIndex.


🧱 Prerrequisitos

  • 🐍 Python 3.9+
  • 🐳 Docker para ejecutar IRIS en un contenedor
  • 🧑‍💻 VS Code para revisar el código
  • 🔑 Clave de API de OpenAI (para el LLM y los embeddings) — o ejecútalo localmente con Ollama si lo prefieres

1) 🧩 Clona el repositorio y prepara un entorno Python

git clone https://github.com/intersystems-ib/customer-support-agent-demo
cd customer-support-agent-demo

python -m venv .venv
# macOS/Linux
source .venv/bin/activate
# Windows (PowerShell)
# .venv\Scripts\Activate.ps1

pip install -r requirements.txt
cp .env.example .env   # añade tu clave de OpenAI

2) 🐳 Arranca InterSystems IRIS (Docker)

docker compose build
docker compose up -d

Abre el Management Portal (http://localhost:52773 en esta demo).


3) 🗃️ Carga los datos estructurados (SQL)

Desde el SQL Explorer de IRIS o tu cliente SQL favorito:

LOAD SQL FROM FILE '/app/iris/sql/schema.sql' DIALECT 'IRIS' DELIMITER ';';
LOAD SQL FROM FILE '/app/iris/sql/load_data.sql' DIALECT 'IRIS' DELIMITER ';';

Este es el esquema que acabas de cargar: image

Ejecuta algunas consultas para familiarizarte con los datos. El agente usará estas tablas para resolver preguntas:

-- Listado de clientes
SELECT * FROM Agent_Data.Customers;

-- Pedidos de un cliente
SELECT o.OrderID, o.OrderDate, o.Status, p.Name AS Product
FROM Agent_Data.Orders o
JOIN Agent_Data.Products p ON o.ProductID = p.ProductID
WHERE o.CustomerID = 1;

-- Info de envío de un pedido
SELECT * FROM Agent_Data.Shipments WHERE OrderID = 1001;

✅ Si ves filas, tu parte estructurada está lista.


4) 📚 Añade conocimiento no estructurado con Vector Search (RAG)

Crea una configuración de embeddings (el ejemplo usa un modelo de OpenAI; ajústalo a tu gusto):

INSERT INTO %Embedding.Config
  (Name, Configuration, EmbeddingClass, VectorLength, Description)
VALUES
  ('my-openai-config',
   '{"apiKey":"YOUR_OPENAI_KEY","sslConfig":"llm_ssl","modelName":"text-embedding-3-small"}',
   '%Embedding.OpenAI',
   1536,
   'a small embedding model provided by OpenAI');

¿Quieres los pasos y opciones exactos? Consulta la documentación

Después, genera los embeddings del contenido de ejemplo:

python scripts/embed_sql.py

Comprueba que los embeddings están en las tablas:

SELECT COUNT(*) AS ProductChunks FROM Agent_Data.Products;
SELECT COUNT(*) AS DocChunks     FROM Agent_Data.DocChunks;

🔎 Bonus: Búsqueda híbrida + vectorial directamente desde SQL con EMBEDDING()

Una gran ventaja de IRIS es que puedes hacer búsqueda semántica (vectorial) dentro de SQL y mezclarla con filtros clásicos—sin microservicios extra. La función EMBEDDING() de SQL crea al vuelo el vector de tu texto de búsqueda y puedes compararlo con vectores almacenados usando operaciones como VECTOR_DOT_PRODUCT.

Ejemplo A — Búsqueda híbrida de productos (filtro por precio + ranking semántico):

SELECT TOP 3
    p.ProductID,
    p.Name,
    p.Category,
    p.Price,
    VECTOR_DOT_PRODUCT(p.Embedding, EMBEDDING('headphones with ANC', 'my-openai-config')) score
FROM Agent_Data.Products p
WHERE p.Price < 200
ORDER BY score DESC

Ejemplo B — Búsqueda semántica de fragmentos de documentos (ideal para alimentar RAG):

SELECT TOP 3
    c.ChunkID  AS chunk_id,
    c.DocID      AS doc_id,
    c.Title         AS title,
    SUBSTRING(c.ChunkText, 1, 400) AS snippet,
    VECTOR_DOT_PRODUCT(c.Embedding, EMBEDDING('warranty coverage', 'my-openai-config')) AS score
FROM Agent_Data.DocChunks c
ORDER BY score DESC

¿Por qué es potente? Puedes pre-filtrar por precio, categoría, idioma, tenant, fechas, etc., y luego ordenar por similitud semántica—todo en una sola sentencia SQL.


5) 🔌 Conecta una API de envíos (mock) con Interoperability

El proyecto expone un pequeño endpoint /api/shipping/status a través de la Interoperabilidad de IRIS — perfecto para simular llamadas “del mundo real”:

curl -H "Content-Type: application/json" \
  -X POST \
  -d '{"orderStatus":"Processing","trackingNumber":"DHL7788"}' \
  http://localhost:52773/api/shipping/status

Ahora abre Visual Trace en el Portal para ver el flujo de mensajes paso a paso (como el radar de un aeropuerto ✈️).


6) 🤖 Conoce al agente (smolagents + tools)

Echa un vistazo a estos archivos:

  • agent/customer_support_agent.py — inicia un CodeAgent y registra las herramientas
  • agent/tools/sql_tool.py — utilidades SQL parametrizadas
  • agent/tools/rag_tool.py — búsqueda vectorial + recuperación de documentos
  • agent/tools/shipping_tool.py — llamadas al endpoint de Interoperability

El CodeAgent planifica qué pasos dar y genera código que llama a tus herramientas. Tú pones las herramientas; él pone el cerebro con un modelo LLM.


7) ▶️ Ejecútalo

Modo one-shot (pruebas rápidas)

python -m cli.run --email alice@example.com --message "Where is my order #1001?"
python -m cli.run --email alice@example.com --message "Show electronics that are good for travel"
python -m cli.run --email alice@example.com --message "Was my headphones order delivered, and what’s the return window?"

CLI interactivo

python -m cli.run --email alice@example.com

Web UI (Gradio)

python -m ui.gradio
# open http://localhost:7860

🛠️ ¿Cómo funciona?

Flujo del agente (simplificado):

  1. 🧭 Planifica cómo resolver la pregunta y qué herramientas usar: p. ej., “comprobar estado del pedido → buscar política de devoluciones”.

  2. 🛤️ Llama a las herramientas según se necesite

    • 🗃️ SQL para clientes/pedidos/productos
    • 📚 RAG sobre embeddings para FAQs/docs (recuerda que puedes prototipar RAG directamente en SQL con EMBEDDING() + operaciones vectoriales como arriba)
    • 🔌 Interoperability API para estado de envíos
  3. 🧩 Sintetiza: compone una respuesta clara y precisa.

Añade o cambia herramientas según crezca tu caso de uso: promociones, garantías, inventario, etc.


🎁 Resumen

Ya tienes un Agente de Atención al Cliente compacto que combina:

  • 🧠 Razonamiento LLM (smolagents CodeAgent)
  • 🗃️ Datos estructurados (IRIS SQL)
  • 📚 Conocimiento no estructurado (IRIS Vector Search + RAG) — con el plus de que EMBEDDING() te permite búsqueda híbrida + vectorial directamente desde SQL
  • 🔌 Llamadas a sistemas en vivo (IRIS Interoperability + Visual Trace)
1
1 60
Artículo Alberto Fuentes · ago 20, 2025 6m read

artisan cover

Si alguna vez habéis observado a un verdadero artesano—ya sea un alfarero que convierte barro en una obra maestra o un luthier que transforma madera en una guitarra maravillosa—sabéis que la magia no está en los materiales, sino en el cuidado, el oficio y el proceso. Lo sé por experiencia propia: mi guitarra eléctrica hecha a mano es una fuente diaria de inspiración, pero lo admito—crear algo así es un talento que no poseo.

Sin embargo, en el mundo digital, veo a menudo gente que espera “magia” de la IA generativa escribiendo prompts vagos y sin contexto como “crea una app”. Los resultados suelen ser frustrantemente superficiales—sin arte ni delicadeza. Demasiados esperan milagros de la IA sin aportar contexto ni estructura alguna.

Esa frustración fue lo que nos motivó a crear dc-artisan—una herramienta para artesanos digitales del prompt. Nuestro objetivo: permitir que cualquiera transforme ideas vagas y soñadoras en obras maestras eficientes, funcionales y ricas en contexto.

Como cuando se observa a un maestro artesano convertir materiales brutos en arte, crear con IA generativa trata de intención, preparación y elaboración cuidadosa. El problema no es la IA en sí—es cómo la utilizamos. Así como un lutier debe elegir y moldear cuidadosamente cada pieza de madera, la ingeniería de prompts eficaz exige contexto claro, estructura e intención.

Creemos que el mundo merece más que “prompts mágicos” que acaban en decepción. Una IA generativa potente surge de una guía humana reflexiva: contexto preciso, objetivos reales y una estructura deliberada. Ningún artesano crea belleza por accidente—los resultados fiables de la IA requieren cuidado y preparación.

dc-artisan aborda la ingeniería de prompts como un verdadero oficio—sistemático, enseñable y comprobable. Ofrece un conjunto completo de herramientas para ir más allá del ensayo, error y conjeturas.

Lo primero que hace dc-artisan es tratar de comprender vuestro prompt como lo haría un colaborador atento. Cuando empezáis a redactar, la herramienta interactúa directamente con vuestra entrada:

  • Preguntas aclaratorias: dc-artisan analiza vuestro prompt inicial y formula preguntas concretas para descubrir vuestro objetivo principal, público objetivo, formato esperado y cualquier contexto faltante. Por ejemplo:

    • “¿Qué tipo de resultado esperáis—resumen de texto, código o datos estructurados?”
    • “¿Quién es el público objetivo?”
    • “¿Con qué tipo de entrada o datos se usará este prompt?”

prompt enhance

Estas interacciones os ayudan a aclarar no solo qué queréis que diga el prompt, sino también por qué.

Una vez que vuestra intención está clara, dc-artisan revisa la estructura y ofrece sugerencias personalizadas—mejorando la claridad, el tono y completando detalles esenciales para obtener resultados ricos en contexto y prácticos.

¿Y lo mejor? Podéis usar todas estas funciones directamente dentro de vuestro editor favorito, ¡VS Code! Podéis insertar variables en vuestro prompt (como {task} o {audience}) para mayor flexibilidad y reutilización, y ver al instante cómo quedan los prompts finales con diferentes sustituciones—así sabéis exactamente cómo funcionará en la práctica.

Pero eso no es todo. dc-artisan también permite afinar los prompts para un rendimiento óptimo. Podéis subir un CSV con casos de prueba para evaluar automáticamente la consistencia, calidad de salida y el impacto de la estructura del prompt en diferentes entradas. dc-artisan evalúa cada respuesta y genera informes completos con puntuaciones de calidad y métricas de similitud—para que podáis medir y optimizar la eficacia de vuestros prompts con total confianza.

testing

Crear prompts sin contexto no es arte — es caos

La ingeniería de prompts sin estructura es como tallar madera con los ojos vendados. Podríais crear algo, pero probablemente no tocará ninguna melodía.

Muchos recurren a prompts vagos o sobrecargados—órdenes cortas y ambiguas o páginas de contenido sin estructura. O el modelo no sabe realmente qué queréis, o se pierde en un mar de ruido.

Cuando el contexto de un prompt se vuelve demasiado largo o desordenado, incluso los modelos avanzados de lenguaje pueden perder el foco. En vez de razonar o generar nuevas estrategias, a menudo se distraen, repitiendo contenido anterior o aferrándose a patrones familiares del inicio de la historia del prompt. Irónicamente, los modelos más grandes con ventanas de contexto más amplias (como 32k tokens) son aún más susceptibles a esto. Simplemente proporcionar más contexto (más documentos, prompts más largos, bases de conocimiento enteras) suele ser contraproducente, causando sobrecarga de contexto, objetivos perdidos y resultados confusos.

Precisamente para cubrir esa brecha está diseñado RAG (Generación aumentada por recuperación): no para dar a los LLMs más información, sino para ofrecerles el conocimiento más relevante en el momento adecuado.

Cómo ayudan dc-artisan y el modo RAG Pipeline

dc-artisan unifica la creación de prompts y la gestión del contexto. No solo os ayuda a escribir mejores prompts; también garantiza que vuestra IA reciba información seleccionada y relevante, no una avalancha de datos sin valor.

Con el modo RAG Pipeline, podéis:

  • 📄 Subir y dividir documentos: PDF, DOCX, Markdown, TXT—fácilmente integrables en vuestra base de datos vectorial.
  • 🧬 Inspeccionar fragmentos: Ver cada unidad atómica de texto incrustado con precisión.
  • 🗑️ Limpieza inteligente: Eliminar contenido no deseado o desactualizado directamente desde la extensión, manteniendo la base de conocimientos de vuestra IA bien cuidada y pertinente.

rag

Este flujo de trabajo está inspirado en el Portal de Ideas de InterSystems (ver DPI-I-557)

Así es como podéis integrar sin problemas una nueva sección sobre la arquitectura del backend de dc-artisan justo antes de “Reflexiones finales”, destacando la integración con InterSystems IRIS Interoperability y nuestro adaptador personalizado liteLLM.

Lo que realmente distingue a dc-artisan es su backend robusto, diseñado para ofrecer tanto interoperabilidad como flexibilidad. El motor de la extensión se ejecuta sobre InterSystems IRIS Interoperability, utilizando un adaptador liteLLM desarrollado por nosotros.

Esta arquitectura significa que no estáis atados a un único proveedor de modelos de lenguaje (LLM). En su lugar, podéis conectaros y cambiar sin interrupciones entre una amplia variedad de plataformas líderes de LLM—incluyendo OpenAI, Gemini, Claude, Azure OpenAI, y otras—todo gestionado desde un backend unificado con calidad empresarial.

Conclusiones Finales

Cada vez más desarrolladores descubren que crear prompts no va de adivinar las “palabras mágicas”. Se trata de tener objetivos bien pensados, lenguaje claro y un contexto potente—escribir prompts como ingenieros, no como magos. Así como los lutieres dan forma a la madera para crear instrumentos con alma, vosotros podéis dar forma a prompts que construyan flujos de trabajo con IA fiables y enriquecidos con contexto, utilizando herramientas creadas para vuestro oficio.

dc-artisan es más que una herramienta—es un cambio de mentalidad, del “vibe coding” hacia la claridad, la precisión y una auténtica artesanía digital.

🎸 ¿Listos para construir prompts con vuestras propias manos? ⚙️ Abrid VS Code, instalad dc-artisan y empezad a crear vuestra IA como artesanos—no como magos. 🗳️ Y si os gusta lo que hemos creado, votad por nosotros en el concurso InterSystems IRIS Dev Tools—¡vuestro apoyo significa mucho!

dc-artisan

0
0 30
Artículo Alberto Fuentes · jul 15, 2025 2m read

En el artículo anterior, os hablamos sobre Python y los cuadros de mando, y en este artículo los compararé entre sí. Si estáis a punto de comenzar con la visualización en Python, la cantidad de librerías y soluciones definitivamente os sorprenderá.

  • matplotlib
  • Seaborn
  • Plotly
  • bokeh
  • Altair
  • Folium

Una visualización en algunas librerías, como Matplotlib, es una imagen estática simple, lo cual es útil para explicar conceptos (en un documento, en diapositivas o en una presentación).

0
0 20
Artículo Alberto Fuentes · jul 4, 2025 3m read

Estoy escribiendo esta publicación principalmente para recopilar un consenso informal sobre cómo los desarrolladores están utilizando Python junto con IRIS, ¡así que por favor responded a la encuesta al final del artículo! En el cuerpo del artículo, daré un poco de contexto sobre cada una de las opciones proporcionadas, así como sus ventajas, pero si lo preferís, podéis hacer una lectura rápida y simplemente responder la encuesta.

0
0 36
Artículo Alberto Fuentes · jun 26, 2025 4m read

Público

Aquellos interesados en explorar nuevos casos de uso de GenerativeAI.

Comparte ideas y razones al entrenar inteligencia artificial generativa para el reconocimiento de patrones.

Desafío 1 – Simple pero no más simple

Un desarrollador aspira a concebir una solución elegante para ciertos requerimientos.
Los patrones de coincidencia (como las expresiones regulares) pueden resolverse de muchas maneras. ¿Cuál es la mejor solución en código?
¿Puede una IA postular una solución elegante de coincidencia de patrones para un rango de ejemplos que van de simples a complejos?

1
0 42
Artículo Alberto Fuentes · jun 11, 2025 3m read

Si estáis migrando de Oracle a InterSystems IRIS, como muchos de mis clientes, podríais encontraros con patrones SQL específicos de Oracle que necesitan ser traducidos.

Tomad este ejemplo:

SELECT (TO_DATE('2023-05-12','YYYY-MM-DD') - LEVEL + 1) AS gap_date
FROM dual
CONNECT BY LEVEL <= (TO_DATE('2023-05-12','YYYY-MM-DD') - TO_DATE('2023-05-02','YYYY-MM-DD') + 1);

En Oracle:

0
0 35
Artículo Alberto Fuentes · jun 3, 2025 3m read

Kong ofrece una herramienta de gestión de configuración de código abierto (escrita en Go), llamada decK, que significa declarative Kong en inglés.

  • Comprobar que deck reconoce vuestra instalación de Kong Gateway mediante deck gateway ping
deck gateway ping   
Successfully connected to Kong!
Kong version:  3.4.3.11

  • Exportar la configuración de Kong Gateway a un archivo llamado "kong.yaml" mediante el comando deck gateway dump
deck gateway dump -o kong.yaml

  • Después de modificar direcciones IP en el archivo kong.yaml, se muestran las diferencias mediante deck gateway diff
0
0 29
Artículo Alberto Fuentes · mayo 28, 2025 4m read

Con el lanzamiento de InterSystems IRIS Cloud SQL, recibimos cada vez más preguntas sobre cómo establecer conexiones seguras mediante JDBC y otras tecnologías de drivers. Aunque contamos con una documentación resumida y detallada sobre las tecnologías de los drivers, nuestra documentación no describe herramientas cliente individuales, como nuestra favorita personal, DBeaver. En este artículo, describiremos los pasos para crear una conexión segura desde DBeaver a vuestra implementación de Cloud SQL.

0
0 30
Artículo Alberto Fuentes · mayo 27, 2025 3m read

Hola desarrolladores:

Al observar la avalancha de herramientas de desarrollo impulsadas por IA y al estilo vibe coding que han estado apareciendo últimamente casi cada mes, con funciones cada vez más emocionantes, me preguntaba si sería posible aprovecharlas con InterSystems IRIS. Al menos para construir un frontend. Y la respuesta es: ¡sí! Al menos con el enfoque que seguí en este ejemplo.

Aquí tenéis mi receta para crear la interfaz de usuario mediante prompts frente al backend de InterSystems IRIS:

  1. Tened una API REST en el lado de IRIS, que refleje una especificación Open API (swagger).
  2. Generad la interfaz de usuario con cualquier herramienta de vibe coding (por ejemplo, Lovable) y apuntad la interfaz al endpoint de la API REST.
  3. ¡Y listo!

Aquí tenéis el resultado de mi propio experimento: una interfaz 100 % generada por prompts frente a la API REST de IRIS, que permite listar, crear, actualizar y eliminar entradas de una clase persistente (Open Exchange, código del frontend, vídeo).

¿Cómo es la receta en detalle?

0
0 35
Artículo Alberto Fuentes · mayo 13, 2025 2m read

A veces, los clientes necesitan una instancia pequeña de IRIS para hacer algo en la nube y luego apagarla, o necesitan cientos de contenedores (es decir, uno por usuario final o uno por interfaz) con cargas de trabajo pequeñas. Este ejercicio surgió para ver cuán pequeña podría ser una instancia de IRIS. Para este ejercicio, nos centramos en cuál es la menor cantidad de memoria que podemos configurar para una instancia de IRIS. ¿Conocéis todos los parámetros que afectan la memoria asignada por IRIS?

Configuración de memoria

0
0 41
Artículo Alberto Fuentes · abr 25, 2025 2m read

Recientemente ayudé a investigar un problema en una instalación que surgió después de que actualizaran su instancia de Caché en Windows de la versión 2015.1 a la 2017.1. Una sesión de terminal iniciada desde el icono del cubo del escritorio del servidor no podía ejecutar comandos a nivel del sistema operativo utilizando la función $ZF(-1). Por ejemplo, al tratar de lanzar  el comando "REM" (que no tiene ningún efecto) de la siguiente manera

write $zf(-1,"rem")

devolvía -1, lo que indicaba que el comando de Windows no se podía ejecutar.

0
0 54
Artículo Alberto Fuentes · abr 11, 2025 2m read

Debido a que la interpretación de SCHEMA por parte de MySQL difiere de la comprensión interpretación común en SQL (como se ve en IRIS, SQL Server u Oracle), nuestro asistente automático de tablas vinculadas puede encontrar errores al intentar recuperar la información de metadatos para construir la tabla vinculada.

(Esto también se aplica a procedimientos y vistas vinculadas)

Al intentar crear una tabla vinculada mediante el asistente, os encontraréis con un error que se parece a esto:

0
0 61
Artículo Alberto Fuentes · abr 9, 2025 4m read

Si queréis saber por ejemplo si ya está implementada una clase sobre un tema en concreto, ahora es posible haciendo una simple pregunta en lenguaje natural. Descargad y ejecutad la aplicación https://openexchange.intersystems.com/package/langchain-iris-tool para conocer todo sobre vuestras clases de proyecto en un chat.

Instalación:

$ git clone https://github.com/yurimarx/langchain-iris-tool.git
$ docker-compose build
$ docker-compose up -d

Uso:

1. Abrid la URL [http://localhost:8501](http://localhost:8501).  

0
0 56
Artículo Alberto Fuentes · feb 28, 2025 3m read

Resumen

Con la ayuda de SQL, podéis crear, entrenar y gestionar modelos de aprendizaje automático directamente en la base de datos con la potente herramienta IntegratedML de InterSystems IRIS. Usando ejemplos de SQL que representan vuestros datos, en este artículo repasaremos la configuración de IntegratedML y su aplicación en situaciones prácticas.

0
0 52
Artículo Alberto Fuentes · feb 20, 2025 2m read

Los ISCLOGs son una herramienta útil de depuración proporcionada con IRIS que os puede ayudar a solucionar muchos tipos de problemas. Para obtener más información sobre cómo generar estos registros, consultad la siguiente documentación de IRIS: "Logging".

Este registro se escribe en el global ^ISCLOG almacenado en el espacio de nombres %SYS. Cada nodo de esta global representa un evento independiente, y en algunos casos, se registra información adicional en los subnodos.

0
0 59
Artículo Alberto Fuentes · ene 27, 2025 5m read

Me alegra anunciar la nueva versión de IoP, que, por cierto, no es solo una línea de comandos. Lo digo porque el nuevo motor de búsqueda con IA aún cree que IoP es solo una línea de comandos. Pero no lo es. Es todo un framework para construir aplicaciones sobre las funcionalidades de interoperabilidad de IRIS con un enfoque Python.

La nueva versión de IoP: 3.2.0 viene con muchas características nuevas, pero la más importante es el soporte para DTL . 🥳

Tanto para mensajes de IoP como para jsonschema. 🎉

image

Soporte para DTL

A partir de la versión 3.2.0, IoP admite transformaciones DTL.

DTL es la Capa de Transformación de Datos (Data Transformation Layer) en interoperabilidad de IRIS.

Las transformaciones DTL se utilizan para convertir datos de un formato a otro mediante un editor gráfico.
También es compatible con estructuras de jsonschema.

Cómo usar DTL con Mensajes

Primero, tenéis que registrar vuestra clase de mensaje en un archivo settings.py.

Para hacerlo, debéis añadir la siguiente línea en el archivo settings.py:

settings.py

from msg import MyMessage

SCHEMAS = [MyMessage]

Luego, podéis usar el comando de migración de IoP para generar los archivos de esquema para vuestras clases de mensaje.

iop --migrate /path/to/your/project/settings.py

Ejemplo

msg.py

from iop import Message
from dataclasses import dataclass

@dataclass
class MyMessage(Message):
    name: str = None
    age: int = None

settings.py

from msg import MyMessage

SCHEMAS = [MyMessage]

Migrad los archivos de esquema

iop --migrate /path/to/your/project/settings.py

Construyendo una transformación DTL

Para construir una transformación DTL, tenéis que crear una nueva clase de transformación DTL.

Id al Portal de Gestión de Interoperabilidad de IRIS y cread una nueva transformación DTL.

image

Luego, seleccionad las clases de mensaje de origen y destino.

image

Y su esquema.

image

Luego, podéis comenzar a construir vuestra transformación.

image

Incluso podéis probar vuestra transformación.

image

Ejemplo de payload para probar como mensaje de origen:

<test>
  <Message>
    <json><![CDATA[
{
"list_str":["toto","titi"],
"post":{"Title":"foo","Selftext":"baz"},
"list_post":[{"Title":"bar","Selftext":"baz"},{"Title":"foo","Selftext":"foo"}]
}
]]></json>
  </Message>
</test>

Soporte para JsonSchema

A partir de la versión 3.2.0, IoP admite estructuras jsonschema para las transformaciones DTL.

Al igual que con las clases de mensaje, necesitáis registrar vuestro jsonschema.

Para hacerlo, debéis invocar este comando de Iris:

zw ##class(IOP.Message.JSONSchema).ImportFromFile("/irisdev/app/random_jsonschema.json","Demo","Demo")

Donde el primer argumento es la ruta al archivo jsonschema, el segundo argumento es el nombre del paquete y el tercer argumento es el nombre del esquema.

Luego, podéis usarlo en vuestra transformación DTL.

El esquema estará disponible con el nombre de Demo.

Ejemplo de archivo jsonschema:

{
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "type": "object",
    "title": "PostMessage",
    "properties": {
        "post": {
            "allOf": [
                {
                    "$ref": "#/$defs/PostClass"
                }
            ]
        },
        "to_email_address": {
            "type": "string",
            "default": null
        },
        "my_list": {
            "type": "array",
            "items": {
                "type": "string"
            }
        },
        "found": {
            "type": "string",
            "default": null
        },
        "list_of_post": {
            "type": "array",
            "items": {
                "allOf": [
                    {
                        "$ref": "#/$defs/PostClass"
                    }
                ]
            }
        }
    },
    "$defs": {
        "PostClass": {
            "type": "object",
            "title": "PostClass",
            "properties": {
                "title": {
                    "type": "string"
                },
                "selftext": {
                    "type": "string"
                },
                "author": {
                    "type": "string"
                },
                "url": {
                    "type": "string"
                },
                "created_utc": {
                    "type": "number"
                },
                "original_json": {
                    "type": "string",
                    "default": null
                }
            },
            "required": [
                "title",
                "selftext",
                "author",
                "url",
                "created_utc"
            ]
        }
    }
}

Ejemplo de Transformación DTL con JsonSchema o Clase de Mensaje

Muchos se pueden encontrar en el paquete UnitTest en el directorio ./src/tests/cls.

Class UnitTest.ComplexTransform Extends Ens.DataTransformDTL [ DependsOn = IOP.Message ]
{

Parameter IGNOREMISSINGSOURCE = 1;

Parameter REPORTERRORS = 1;

Parameter TREATEMPTYREPEATINGFIELDASNULL = 0;

XData DTL [ XMLNamespace = "http://www.intersystems.com/dtl" ]
{
<transform sourceClass='IOP.Message' targetClass='IOP.Message' sourceDocType='registerFilesIop.message.ComplexMessage' targetDocType='registerFilesIop.message.ComplexMessage' create='new' language='objectscript' >
<assign value='source.{post}' property='target.{post}' action='set' />
<foreach property='source.{list_str()}' key='k1' >
<assign value='source.{list_str(k1)}_"foo"' property='target.{list_str()}' action='append' />
</foreach>
<foreach property='source.{list_post()}' key='k2' >
<assign value='source.{list_post().Title}' property='target.{list_post(k2).Title}' action='append' />
</foreach>
</transform>
}

}

Nueva documentación

IoP viene con una nueva documentación, la cual está disponible en https://grongierisc.github.io/interoperability-embedded-python/.

Allí encontraréis toda la información que necesitáis para comenzar a usar IoP.

image

Espero que disfrutéis esta nueva versión de IoP. 🎉

0
0 71
Artículo Alberto Fuentes · ene 9, 2025 1m read

Cuando desplegáis código desde un repositorio, la eliminación de clases (archivos) podría no reflejarse en vuestro sistema CICD. Por ejemplo si en vuestro repositorio elimináis una clase y queréis que se refleje en el código desplegado.
Aquí tenéis un sencillo comando de una sola línea para eliminar automáticamente todas las clases en un paquete específico que no hayan sido importadas. Se puede ajustar fácilmente para una variedad de tareas adicionales:

set packages = "USER.*,MyCustomPackage.*"set dir = "C:\InterSystems\src\"set sc = $SYSTEM.OBJ.LoadDir(dir,"ck", .err, 1, .loaded)
set sc = $SYSTEM.OBJ.Delete(packages _ ",'" _ $LTS($LI($LFS(loaded_",",".cls,"), 1, *-1), ",'"),, .err2)

El primer comando compila las clases y también devuelve una lista de las clases cargadas.
El segundo comando elimina todas las clases de los paquetes especificados, excepto las clases que se cargaron justo antes en el paso anterior.

0
0 71
Artículo Alberto Fuentes · dic 3, 2024 2m read

La auditoría es una capacidad fundamental para garantizar la seguridad de vuestro servidor, y desde hace un tiempo hemos incluido la posibilidad de auditar las sentencias SQL ejecutadas en el servidor.

Con la versión 2024.3 ya disponible, ahora ofrecemos opciones más detalladas para definir los eventos que se auditarán.

0
0 52
Artículo Alberto Fuentes · nov 27, 2024 1m read

La capacidad de reenviar mensajes fácilmente siempre ha sido una de las características más destacadas de nuestras capacidades de interoperabilidad.

Con la versión 2024.3 que se lanzará pronto (ya disponible como Developer Preview), ¡lo hemos hecho aún más sencillo!

0
0 71
Artículo Alberto Fuentes · oct 28, 2024 3m read

Hola a todos, hoy comentamos con un ejemplo cómo solucionar problemas relacionados con el TimeOut cuando se hacen llamadas a servicios SOAP.

Tomemos como ejemplo aplicaciones que proporcionen servicios SOAP que utilizan consultas SQL basadas en "DSTIME" para devolver registros que han sido añadidos o modificados recientemente. Como estos registros no cambian con frecuencia, estas consultas suelen devolver un número reducido de registros, lo cual implica un tiempo de procesamiento corto.

0
0 167
Artículo Alberto Fuentes · oct 21, 2024 3m read

¡Muy buenas a todos! Os paso un ejemplo de resolución de problemas a la hora de implementar transformaciones de datos que es muy interesante: estaba trabajando en un transformación de datos (DTL) de mensajes HL7 pero no paraba de obtener errores del tipo ERROR #5002... MAXSTRING. El problema era que la mayor parte de acciones en la interfaz gráfica de DTL utilizan el tipo de datos %String al trabajar con segmentos de un mensaje HL7.

Un %String tiene un límite de 3,641,144 caracteres, y mi OBX5.1 tenía 5,242,952 caracteres de longitud, como en el ejemplo proporcionado. Por supuesto, el administrador del sistema PACS dijo que se necesitaban archivos de ultra alta calidad, incluyendo resolución 4K, por lo que no pudimos lograr que el proveedor comprimiera o reformateara estos archivos a jpg comprimido o algo similar.

Inicialmente, este proveedor envía un ORU^R01 en la versión 2.3, y el EHR espera un MDM^T02 también en la versión 2.3. Además, necesitábamos realizar las siguientes transformaciones:

  1. La imagen incrustada se enviaba en OBX-5.1, y necesitábamos moverla a OBX-5.5.
  2. El formato de la imagen se enviaba en OBX-6, y lo necesitábamos en OBX-5.3 y OBX-5.4.
  3. Necesitábamos crear un segmento TXA.
  4. Soportar un conjunto de 25 segmentos OBX que podían estar completamente vacíos (>25 x 5Mb = ¡más de 125Mb de tamaño de mensaje!).

Ejemplo de estructura de mensaje recibido (cada "..." quedaría reemplazado con datos de más de 5 MB):

MSH|^~\&|VENDOR||||20241017125335||ORU^R01|1|P|2.3|
PID|||203921||LAST^FIRST^^^||19720706|M||||||||||100001|
PV1||X||||||||GI6|||||||||100001|
ORC|RE||21||SC||1|||||||||||
OBR|1||21|21^VENDOR IMAGES|||20241017123056|||||||||1001^GASTROENTEROLOGY^PHYSICIAN|||||Y||||F||1|
OBX|1|PR|100001|ch1_image_001.bmp|...^^^^^^^|BMP|||||F|
OBX|2|PR|100001|ch1_image_003.bmp|...|BMP|||||F|
OBX|3|PR|100001|ch1_video_01thumbnail.bmp|...|BMP|||||F|
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||

Las herramientas y métodos de prueba habituales no estaban a la altura para manejar mensajes tan grandes. Cuando usaba este ejemplo reemplazando los datos con ..., la interfaz gráfica de DTL de arrastrar y soltar funcionaba bien, pero al insertar los datos reales, todo se venía abajo.

Finalmente, descubrí que tenía que usar un bloque de código con ObjectScript y los tipos de datos Stream para poder trabajar correctamente con estos mensajes grandes.

Comparto mi clase final de DTL para cualquiera que venga después de mí y pueda encontrar esto útil:

Class OrdRes.VendorMDM Extends Ens.DataTransformDTL [ DependsOn = EnsLib.HL7.Message ]
{

Parameter IGNOREMISSINGSOURCE = 1;

Parameter REPORTERRORS = 1;

Parameter TREATEMPTYREPEATINGFIELDASNULL = 0;

XData DTL [ XMLNamespace = "http://www.intersystems.com/dtl" ]
{
<transform sourceClass='EnsLib.HL7.Message' targetClass='EnsLib.HL7.Message' sourceDocType='2.3:ORU_R01' targetDocType='2.5:MDM_T02' create='new' language='objectscript' >
<assign value='source.{MSH}' property='target.{MSH}' action='set' />
<assign value='"MDM"' property='target.{MSH:MessageType.MessageCode}' action='set' />
<assign value='"T02"' property='target.{MSH:MessageType.TriggerEvent}' action='set' />
<assign value='"2.5"' property='target.{MSH:VersionID.VersionID}' action='set' />
<assign value='source.{MSH:DateTimeofMessage}' property='target.{EVN:2}' action='set' />
<assign value='source.{PIDgrpgrp().PIDgrp.PID}' property='target.{PID}' action='set' />
<assign value='source.{PIDgrpgrp().PIDgrp.PV1grp.PV1}' property='target.{PV1}' action='set' />
<assign value='source.{PIDgrpgrp().ORCgrp().ORC}' property='target.{ORCgrp().ORC}' action='set' />
<assign value='source.{PIDgrpgrp().ORCgrp().OBR}' property='target.{ORCgrp().OBR}' action='set' />
<assign value='source.{PIDgrpgrp().ORCgrp().NTE()}' property='target.{ORCgrp().NTE()}' action='set' />
<assign value='"Endoscopy Image"' property='target.{TXA:DocumentType}' action='set' />
<assign value='"AU"' property='target.{TXA:DocumentCompletionStatus}' action='set' />
<assign value='"AV"' property='target.{TXA:DocumentAvailabilityStatus}' action='set' />
<assign value='source.{PID:18}' property='target.{TXA:12.3}' action='set' />
<code>
<![CDATA[
 set OBXCount=source.GetValueAt("PIDgrpgrp(1).ORCgrp(1).OBXgrp(*)")
 For k1 = 1:1:OBXCount
 {
   // if OBX-1 is empty then it is assumed the rest of the segment will be empty too, so disregard it.
   If source.GetValueAt("PIDgrpgrp(1).ORCgrp(1).OBXgrp("_k1_").OBX:SetIDOBX") '= ""
   {
     // create new stream to read source OBX
     set srcOBXStream=##class(%Stream.GlobalCharacter).%New()
     // get stream data from source OBX
     set tSC=source.GetFieldStreamRaw(srcOBXStream,"PIDgrpgrp(1).ORCgrp(1).OBXgrp("_k1_").OBX")
     // get the positions of needed delimitters:
     set p1=srcOBXStream.FindAt(1,"|")    // 0>p1="OBX"
     set p2=srcOBXStream.FindAt(p1+1,"|") // p1>p2=OBX-1
     set p3=srcOBXStream.FindAt(p2+1,"|") // p2>p3=OBX-2
     set p4=srcOBXStream.FindAt(p3+1,"|") // p3>p4=OBX-3
     set p5=srcOBXStream.FindAt(p4+1,"|") // p4>p5=OBX-4
     set p6=srcOBXStream.FindAt(p5+1,"^") // p5>p6=OBX-5.1
     set p7=srcOBXStream.FindAt(p6+1,"|") // p6>p7=OBX-5.2 -> OBX 5.*
     // if no OBX-5.2 then there will not be the `^` and p6 and p7 will be `-1`
     // when that is the case, find p7 starting at `p5+1` and make p6 = p7
     if (p6 < 0) {
       set p7=srcOBXStream.FindAt(p5+1,"|") // p5>p7=OBX-5
       set p6=p7
     }
     set p8=srcOBXStream.FindAt(p7+1,"|") // p7>p8=OBX-6
     set tStream=##class(%Stream.GlobalCharacter).%New()

     // renumber OBX-1 to OBX 
     set tSC=tStream.Write("OBX|"_k1_"|")
     
     // set OBX2-2 to "ED"
     set tSC=tStream.Write("ED|")
     
     // copy source OBX-3 to target OBX-3
     set tSC=srcOBXStream.MoveTo(p3+1)
     set tSC=tStream.Write(srcOBXStream.Read(p4-p3-1))
     set tSC=tStream.Write("|")
     
     // copy source OBX-4 to target OBX-4
     set tSC=srcOBXStream.MoveTo(p4+1)
     set tSC=tStream.Write(srcOBXStream.Read(p5-p4-1))
     
     // copy source OBX-6 to OBX-5.3 & OBX-5.4
     set tSC=srcOBXStream.MoveTo(p7+1)
     set docType=srcOBXStream.Read(p8-p7-1)
     set tSC=tStream.Write("|^^"_docType_"^"_docType_"^")
     
     // copy source OBX-5.1 to target OBX-5.5
     // can only set up to 3,641,144 chars at once, so do while loop...
     set startPos=p5+1
     set remain=p6-p5-1
     // characters to read/write in each loop, max is 3,641,144 since .Write limit is a %String
     set charLimit=3000000
     while remain > 0 {
       set tSC=srcOBXStream.MoveTo(startPos)
       set toRead = charLimit
       if toRead > remain {
         set toRead=remain
       }
       set tSC=tStream.Write(srcOBXStream.Read(toRead))
       set remain=remain-toRead
       set startPos=startPos+toRead
     }
     set tSC=tStream.Write("|")

     set obxSegment=##class(EnsLib.HL7.Segment).%New()
     set obxSegment.SegType="2.5:OBX"
     set tSC=obxSegment.StoreRawDataStream(tStream)
     set tSC=target.setSegmentByPath(obxSegment,"OBXgrp("_k1_").OBX")
   }
 }
]]></code>
</transform>
}

}

Para desarrollar y probar esto, utilicé los plugins de VSCode para InterSystems porque las herramientas de prueba de integración no podían manejar el tamaño del mensaje.

También añadiré que, para enviar HL7 sobre HTTPS a InterConnect de Epic, fue necesario crear una clase HTTP personalizada y enviar el tipo de contenido personalizado x-application/hl7-v2+er7.

0
0 114
Artículo Alberto Fuentes · sep 23, 2024 8m read

¡Hola, desarrolladores!

En esta serie de artículos hemos hablado del framework iris-datapipe, de cómo nos ayuda a crear "pipes" de datos para la ingesta y procesamiento, y de cómo instalarlo. Vamos a profundizar en cómo implementar uno de esos "pipes" paso a paso.

Si llegaste directamente a este artículo, te recomiendo revisar los anteriores y recordar que iris-datapipe incluye un QuickStart para que puedas explorar sus funcionalidades rápidamente 👌.

El ejemplo que abordaremos está incluido en el QuickStart, por lo que puedes utilizarlo como referencia.

Definir un nuevo Pipe

Comienza por definir un nuevo Pipe en la interfaz gráfica. Básicamente, solo se trata de asignar un código y una descripción.

Opcionalmente, puedes especificar un recurso de seguridad de IRIS que se requerirá para poder operar con ese Pipe (esto es útil si necesitas crear pipes que solo sean accesibles a determinados usuarios).

En el ejemplo, definimos un pipe llamado REST-API, que se encargará de procesar datos recibidos desde una API REST, donde llegarán datos sobre personas.

image

Procesamiento de los datos

Para procesar los datos utilizando un "pipe", necesitamos seguir los siguientes pasos:

1) Definir un modelo DataPipe

Debemos definir un modelo para los datos que queremos procesar.

Un modelo no es más que una clase que hereda o extiende de DataPipe.Model.cls donde tendrás que implementar algunos métodos.

Tu modelo debe implementar:

  • Cómo serializar/deserializar tus datos (por ejemplo, usando XML o JSON).
  • Cómo normalizar y validar tus datos.
  • Y finalmente, qué operación quieres ejecutar sobre tus datos una vez están normalizados y validados.

En mi ejemplo, el modelo que utilizaré será DataPipe.Test.REST.Models.Person.cls.

Dado que el modelo es una clase convencional en InterSystems IRIS, puedes añadir herencia u otro comportamiento que necesites. En mi caso, heredo de DataPipe.Test.REST.Msg.PersonData, donde tengo definidas las propiedades que me interesa tratar.

El modelo DataPipe.Test.REST.Models.Person.cls tiene diferentes métodos:

  • Serialize, Deserialize: Se utilizan para indicar cómo serializar/deserializar el modelo. En este caso, utilizo JSON.
  • Normalize: Especifica cómo quiero normalizar el modelo. En mi caso, solo quiero llamar a una transformación de datos.
/// Normalize model
Method Normalize(Output obj As DataPipe.Model) As %Status
{
    set ret = $$$OK
    try {
        // call normalizaton data transform
        set sc = $classmethod("DataPipe.Test.REST.DT.PersonNormalize", "Transform", $this, .obj)
        $$$ThrowOnError(sc)

    } catch ex {
        set ret = ex.AsStatus()
    }
    quit ret
}
  • Validate: Indica cómo quiero validar si mi modelo es correcto o no. Puedo añadir "warnings" también. Puedes implementar lo que necesites:
/// Validate model
Method Validate(Output errorList As %List) As %Status
{
    #define AddError(%list, %code, %desc) set error = ##class(DataPipe.Data.ErrorInfo).%New() set error.Code=%code set error.Desc=%desc do %list.Insert(error)
	
    set ret = $$$OK
    try {
        set errorList = ##class(%ListOfObjects).%New()

        // date of birth
        if ..DOB="" { 
            $$$AddError(errorList, "V001", "DOB required")
        } else {
            set yearDOB = $extract($zdate(..DOB,8),1,4)
            if (yearDOB < 1930) $$$AddError(errorList, "V002", "DOB must be greater than 1930")
            if (yearDOB < 1983) $$$AddError(errorList, "W083", "Warning! Older than 1983")
        }

        // model is invalid if errors (not warnings) found
        for i=1:1:errorList.Count() {
            set error = errorList.GetAt(i)
            set errorCode = error.Code

            // in this sample model, all warnings start with "W"
            if errorCode'["W" {
                $$$ThrowStatus($$$ERROR($$$GeneralError, "Invalid"))
            }
		}
       
    } catch ex {
        set ret = ex.AsStatus()
    }

    quit ret
}
  • GetOperation: Indica qué Business Operation quiero que ejecute la operación sobre los datos (lo entenderás mejor cuando comentemos los componentes de interoperabilidad).
/// Return the Business Operation name that will run the operation with the model
/// Each Business Operation can be used to hold different queues
Method GetOperation() As %Status
{
    quit "Person Operation"
}
  • RunOperation: Esta es la operación que quiero ejecutar sobre mis datos. Puedo guardarlos en la base de datos o llamar a otro componente de interoperabilidad para continuar el procesamiento más adelante. En mi ejemplo, los guardo en una global y también los envío a otro Business Process.
/// Run final operation with the model
/// This method can be used to persit data from the model to an operational data store
Method RunOperation(Output errorList As %List, Output log As %Stream.Object, bOperation As Ens.BusinessOperation = "", Output delayedProcessing As %Boolean = 0) As %Status
{
    #define AddError(%list, %code, %desc) set error = ##class(DataPipe.Data.ErrorInfo).%New() set error.Code=%code set error.Desc=%desc do %list.Insert(error)
    #define AddLog(%log, %msg) do %log.WriteLine("["_$zdt($h,3)_"] "_%msg)
	
    set errorList = ##class(%ListOfObjects).%New()
    set log = ##class(%Stream.GlobalCharacter).%New()

    set ret = $$$OK
    try {
        TSTART
        $$$AddLog(log, "Transaction Started")

        // simulate an operation error
        if ##class(Ens.Util.FunctionSet).In(..Name, ##class(DataPipe.Test.HL7.Helper).OperationErrorNames()) {
            $$$ThrowStatus($$$ERROR($$$GeneralError, "Simulated Operation Error"))
        }

        // store serialized model
        $$$ThrowOnError(..Serialize(.stream))
        set ^zDataPipe($i(^zDataPipe)) = stream.Read()
        $$$AddLog(log, "Model Stored in ^zDataPipe("_$get(^zDataPipe)_")")

        TCOMMIT
        $$$AddLog(log, "Transaction Commited")

        // you can send messages to other production components (while you are not on an open transaction)
        // you can use this feature to continue processing the record in other component (delayed processing)
        set delayedProcessing = 1
        if $isobject(bOperation) {
            set req = bOperation.OperRequest
            $$$ThrowOnError(bOperation.SendRequestAsync("REST Delayed Oper Update", req))
        }

    } catch ex {
        TROLLBACK 
        $$$AddLog(log, "Rollback!")

        set ret = ex.AsStatus()
        $$$AddLog(log, "Error catched: "_$system.Status.GetOneStatusText(ret))

        // include exception errors into errorList
        do $system.Status.DecomposeStatus(ret, .errors)
		for i=1:1:errors {
			$$$AddError(errorList, "Exception", errors(i))
		}
    }
    quit ret
}

2) Añadir componentes de interoperabilidad

Después de definir tu modelo, necesitas configurar una producción de interoperabilidad usando componentes de DataPipe. iris-datapipe incluye componentes preconstruidos que pueden operar con un modelo DataPipe como el que hemos definido anteriormente.

El único proceso que debes implementar es el proceso de ingestión.

2.1) Crear un Proceso de Ingestión

Necesitas crear un nuevo Business Process que utilice como contexto DataPipe.Ingestion.BP.IngestionManagerContext.

Este proceso recibirá la entrada de datos que decidas (en este ejemplo, el mensaje que envía una API REST que actúa como Business Service) y debe implementar:

Identificación de los datos que procesas (InboxAttributes):

  • Debes identificar el registro que estás procesando y proporcionar los InboxAttributes.
  • El "pipe" al que pertenece este registro debe indicarse en este momento.
  • Estos atributos describirán el registro que estás tratando y luego se utilizarán para la búsqueda desde la interfaz gráfica.
  • Para realizar lo anterior, puedes utilizar transformaciones de datos, código, ¡lo que necesites!

Convertir los datos de entrada en el modelo de datos que definiste previamente:

  • Debes utilizar transformaciones, código o lo que prefieras para transformar la información de entrada al modelo de datos que has desarrollado previamente.

image

2.2) Añadir el resto de componentes

El resto de los componentes de interoperabilidad son proporcionados por iris-datapipe y ya están preconstruidos. Estos componentes llamarán a los distintos métodos implementados en tu modelo.

En general, necesitarás añadir a la producción, por cada "pipe" diferente que quieras implementar:

Aquí tienes la producción de ejemplo que se utiliza en el QuickStart.

Con todo esto, cuando comiencen a llegar datos y se procesen, podrás verlos directamente desde la interfaz gráfica.

image

¡Espero que os sea útil!

0
0 74
Artículo Alberto Fuentes · sep 20, 2024 5m read

¡Hola a todos de nuevo!

En el artículo anterior hablamos de iris-datapipe, un framework diseñado para ayudarte a definir "pipes" de datos con un patrón de ingesta y procesamiento. Hoy veremos cómo puedes instalarlo y configurarlo paso a paso.

iris-datapipe incluye un QuickStart, que te permite probarlo rápidamente utilizando un contenedor Docker preconfigurado.

Pero, ¿qué debes hacer si quieres usarlo en tu propia instancia?

InterSystems IRIS

Instalación del paquete

s r=##class(%Net.HttpRequest).%New(),r.Server="pm.community.intersystems.com",r.SSLConfiguration="ISC.FeatureTracker.SSL.Config" d r.Get("/packages/zpm/latest/installer"),$system.OBJ.LoadStream(r.HttpResponse.Data,"c")
zpm "install iris-datapipe"

Configuración de usuarios y seguridad

iris-datapipe requiere ciertos recursos de seguridad:

  • DP_ADMIN - Administrador de DataPipe.
  • DP_MENU_DASHBOARD - Acceso al menú "Dashboard" en la interfaz gráfica.
  • DP_MENU_SEARCH - Acceso al menú "Search" en la interfaz gráfica.

Necesitarás crear un usuario con acceso a la interfaz gráfica y que utilice estos recursos. A continuación, te muestro un ejemplo de cómo hacerlo:

Crear recursos de seguridad:

zn "%SYS"
write ##class(Security.Resources).Create("DP_ADMIN","DataPipe Admin Privilege")
write ##class(Security.Resources).Create("DP_MENU_DASHBOARD","DataPipe UI Dashboard Menu Access")
write ##class(Security.Resources).Create("DP_MENU_SEARCH","DataPipe UI Search Menu Access")

Crear un nuevo rol llamado DataPipe_Admin:

(¡Importante! Debes otorgarle permisos sobre la base de datos donde hayas instalado iris-datapipe. En este ejemplo, lo hago sobre %DB_USER)

write ##class(Security.Roles).Create("DataPipe_Admin","DataPipe Administrator","DP_ADMIN:RWU,DP_MENU_DASHBOARD:RWU,DP_MENU_SEARCH:RWU,%DB_USER:RW,%DB_IRISSYS:R")

Añadir privilegios de SQL sobre tablas y vistas necesarias al rol DataPipe_Admin:

GRANT INSERT,SELECT,UPDATE ON DataPipe_Data.Pipe, DataPipe_Data.Preference TO DataPipe_Admin
GRANT SELECT ON DataPipe_Data.VInbox, DataPipe_Data.VIngestion, DataPipe_Data.VStaging, DataPipe_Data.VOper TO DataPipe_Admin

Crear un usuario de ejemplo dpadmin con la contraseña demo y asignarlo al rol DataPipe_Admin:

write ##class(Security.Users).Create("dpadmin","DataPipe_Admin","demo")

Otras consideraciones

Para que la interfaz gráfica funcione correctamente, es importante tener en cuenta la configuración de CORS (Cross-Origin Resource Sharing) y los permisos sobre la base de datos donde tengas instalado DataPipe.

CORS (Cross-Origin Resource Sharing)

El navegador que utilices para acceder a la interfaz web enviará solicitudes a tu servidor InterSystems IRIS. Como parte de este proceso, el navegador preguntará a IRIS si está permitido realizar estas solicitudes desde su origen (esto es, desde donde estés sirviendo la aplicación web). Puedes encontrar más información en la documentación oficial.

Una manera sencilla de permitir solicitudes desde cualquier origen (incluido localhost) es utilizar directamente esta clase que se usa en el entorno de QuickStart.

Sin embargo, ten en cuenta que siempre debes restringir los posibles orígenes antes de poner un sistema en producción. En el ejemplo proporcionado, necesitarías modificar el método OnHandleCorsRequest.

Permisos sobre la base de datos

Es necesario que las solicitudes web que lleguen desde el WebGateway, como CSPSystem, tengan permiso para leer la base de datos donde está instalado iris-datapipe.

En mi ejemplo, una forma sencilla de lograrlo es otorgarle directamente permisos a CSPSystem sobre %DB_USER.

Interfaz gráfica

La interfaz gráfica es una aplicación Angular que puedes encontrar aquí: iris-datapipeUI.

Si tienes conocimientos de Angular, no necesitarás mucha explicación. Solo debes saber que puedes hacer un fork/duplicado del repositorio y adaptarlo según tus necesidades.

Si no estás familiarizado con Angular, puedes seguir estos pasos utilizando Docker para simplificar el proceso:

  • Clona o descarga el repositorio iris-datapipeUI.
  • Modifica el archivo environment.ts para sustituir las URLs de IRIS por las de tu entorno.
  • Prepara una imagen Docker con la aplicación construida:
docker compose build 
  • Ejecuta un contenedor con la imagen que acabas de preparar:
docker compose up -d

Si has seguido los pasos y todo va bien, deberías poder hacer login en la interfaz gráfica con tu usuario dpadmin / demo y verás un entorno vacío: image

En un próximo artículo hablaremos de cómo crear e implementar "pipes" en iris-datapipe.

0
0 73
Artículo Alberto Fuentes · sep 18, 2024 2m read

¡Hola a todos!

Me alegra anunciaros una nueva versión de iris-datapipe, un framework potente que, acompañado de una interfaz gráfica, te permitirá obtener una visión integral de la ingesta y procesamiento de datos en InterSystems IRIS.

¿Por qué iris-datapipe?

En múltiples proyectos, nos dimos cuenta de que repetíamos un patrón común: ingestión de datos de diversas fuentes, enriquecimiento al combinarlos con otros datos, normalización, aplicación de reglas de validación y, finalmente, la realización de alguna acción u operación sobre estos datos (por ejemplo, almacenarlos o enviarlos a otro sistema).

Si bien siempre hemos utilizado las potentes capacidades de interoperabilidad de InterSystems IRIS para implementar estos proyectos, notábamos la necesidad de una visión más integral y lógica del flujo de datos más allá de los múltiples mensajes individuales entre los componentes de interoperabilidad.

Queríamos poder buscar los datos por conceptos de negocio (por ejemplo, un número de registro o lote) y también disponer de una manera sencilla y unificada de entender qué había sucedido con ellos: si estaban en proceso, si alguna regla de validación falló, si ocurrió un error en la comunicación o si fueron procesados correctamente.

image

Para resolver estos desafíos, nació iris-datapipe. Este framework nos permite definir "pipes" de datos que siguen ese patrón de ingesta y procesamiento, todo ello aprovechando las funcionalidades de interoperabilidad de la plataforma, y reduciendo significativamente el esfuerzo requerido. 😊

¿Cómo empezar?

En los próximos días, publicaré otro artículo donde detallaré el proceso de instalación y cómo comenzar a utilizar iris-datapipe.

Mientras tanto, te invito a explorar algunas de las funcionalidades que puedes probar tú mismo con el QuickStart incluido en la aplicación:

image

image

0
0 101
Artículo Alberto Fuentes · sep 4, 2024 2m read

Hola Comunidad,

Este artículo tiene como objetivo guiaros a través del proceso de configuración y utilización de la Funcionalidad de Entorno de Ejecución Flexible de Python para Python embebido. Antes de la versión 2024.2, el instalador de Intersystems IRIS para Windows incluía una versión pre-instalada de Python. Podíais encontrar las bibliotecas de Python y los archivos de la aplicación ubicados en el directorio \lib\python dentro de la carpeta de instalación de IRIS (por ejemplo, C:\InterSystems\IRIS20242\lib\python).

0
0 78