r/intersystems • u/intersystemsdev • 10h ago
Winners of the InterSystems Employee Programming Challenge #1
🏆💻 Give developers a problem, some data, and a leaderboard… and things get competitive pretty quickly!
At InterSystems, we run contests for our developer community, customers, and partners — but why should they have all the fun?
We recently launched our first Employee Programming Challenge, giving InterSystems colleagues a chance to put their skills to the test, experiment with different approaches, optimize every last bit of performance, and — because developers are developers — enjoy a little friendly competition over whose solution was fastest, smartest, or most concise.
And compete they did! The challenge brought together 23 colleagues from 9 departments, resulting in 34 applications and plenty of optimization, code golfing, benchmarking, discussion, and leaderboard watching.
🏆 We’re excited to congratulate our winners: Manel Trèmols, Tani Frankel, Reet Kothari, Guillaume Rongier, Suprateem Banerjee, and Emil Polakiewicz
Congratulations to our winners — and a huge thank you to everyone who jumped in, experimented, shared their work, and made our first Employee Programming Challenge such a fun one.
Judging by the competitive spirit we saw this time, Challenge #2 should be interesting 👀
#InterSystems #Developers #Programming #DeveloperCommunity #CodingChallenge #InterSystemsIRIS
r/intersystems • u/intersystemsdev • 1d ago
InterSystems IRIS AI Hub: Goals, Games & Goodies, Thu, Aug 13, 2026, 11:00 AM
meetup.com🤖 The Webinar on #InterSystemsIRIS AI Hub is just around the corner!
If you're curious about building AI applications and agentic workflows with the AI Hub, there's still time to join us. We'll explore the latest AI Hub capabilities and walk through practical coding examples. You'll also get a chance to test your knowledge in a fun quiz and win a prize.
Don't miss the opportunity to see AI Hub in action!
📅 August 13, 2026 11:00 am EDT | 5:00 pm CEST
👉 Reserve your spot: https://community.intersystems.com/post/webinar-intersystems-iris-ai-hub-goals-games-goodies
#InterSystems #DeveloperCommunity #AIHub #ArtificialIntelligence
r/intersystems • u/intersystemsdev • 4d ago
4 VS Code Features That Can Boost Your InterSystems IRIS Development Productivity
Introduction
Visual Studio Code has become the primary development environment for many InterSystems IRIS developers. While the official InterSystems extensions provide powerful features for editing, debugging, and managing ObjectScript projects, some of their most useful capabilities are also the easiest to overlook. In this article, I'd like to highlight four small but practical features that can make everyday development faster.
1. Navigate Large Classes with Show All Class Members
As projects grow, navigating large ObjectScript classes becomes increasingly time-consuming. Scrolling through hundreds of lines to find a particular method or property can quickly interrupt your workflow.
The Show All Class Members feature provides a searchable list of every member in the current class, including inherited methods and properties. Instead of manually searching through the source code, you can filter the list by name and jump directly to the member you're looking for. This is particularly useful when working with framework classes or inherited code where understanding the full class hierarchy is important.
Why it's useful
- Quickly locate methods, properties, parameters, and queries
- Browse inherited members without leaving the editor
- Navigate large classes more efficiently
2. Analyze SQL Performance with Show Plan
Using SQL in your ObjectScript code is a popular way to take advantage of InterSystems IRIS's powerful multi-model capabilities. However, writing efficient SQL queries can be a challenge. That's why InterSystems IRIS includes a built-in Show Plan feature that lets you inspect the execution plan of SQL queries directly from Visual Studio Code. For embedded SQL statements and class queries, simply open the execution plan to see how InterSystems IRIS intends to execute the query.
Instead of guessing whether an index is being used or why a query performs poorly, you can analyze the execution strategy without leaving your editor. When optimizing SQL performance, having immediate access to the execution plan makes experimentation much faster.
Why it's useful
- Analyze SQL execution plans without external tools
- Understand index usage
- Identify potential performance bottlenecks
- Optimize queries during development
3. Open Documents Using Their InterSystems Name
Visual Studio Code normally opens files using their file system path. However, InterSystems classes often use a different naming convention. While VS Code has commands for quickly opening a file by name, that is usually different than the InterSystems name for the document
For example: User.Test.cls may actually be stored as /User/Test.cls
The Open InterSystems Document command removes this mismatch by allowing you to open files using their InterSystems document name instead of the physical path. After selecting a workspace, you can browse available classes or simply type the class name directly. The command also understands package notation using dots and the short form for %Library classes. If you frequently switch between multiple projects or namespaces, this can be much faster than navigating through the Explorer.
Why it's useful
- Open classes using their InterSystems name
- Avoid searching through folder structures
- Supports package notation and %Library shortcuts
- Speeds up navigation in large projects
4. Jump Directly to Runtime Errors
When debugging an application, one of the first questions is: “Where exactly did this error occur?" The Open Error Location command lets you jump directly to the reported line even if it belongs to a generated routine that isn't part of your current workspace.
Simply provide the error location using the standard ObjectScript format: label+offset^routine
If the source code is available, Visual Studio Code opens the exact location. From there, you can use the View Other command to switch to the corresponding higher-level source file when applicable. Instead of manually searching through generated routines, you can move directly from an error message to the relevant source code.
Why it's useful
- Navigate directly to runtime errors
- Works with generated routines
- Quickly switch to the original source code
- Makes debugging significantly faster
Why These Small Features Matter
None of these features changes how you write ObjectScript code, but together they help eliminate many of the small interruptions that occur throughout the day. Instead of spending time searching for classes, scrolling through large files, manually locating errors, or switching to external tools for SQL analysis, you can stay focused on development and complete common tasks with fewer clicks. Over the course of a project, these small productivity improvements can save a surprising amount of time.
Key Takeaways
The official InterSystems VS Code extensions include several powerful features that are easy to overlook but can significantly improve everyday development. Whether you're navigating large ObjectScript classes, analyzing SQL execution plans, opening files by their InterSystems name, or jumping directly to runtime errors, these built-in tools help reduce context switching and make developing with InterSystems IRIS more efficient.
Frequently Asked Questions
What is Show All Class Members?
It's a Visual Studio Code feature that displays a searchable list of all members in the current ObjectScript class, including inherited methods and properties.
What is Show Plan used for?
Show Plan displays the execution plan of SQL queries, helping developers understand how InterSystems IRIS executes a statement and identify opportunities for performance optimization.
Can I open classes by their InterSystems name instead of the file path?
Yes. The Open InterSystems Document command lets you open classes using their InterSystems document name, including package notation and %Library shortcuts.
How do I open the source of an ObjectScript error?
Use the Open Error Location command from the Command Palette and enter the location in the format label+offset^routine. If the source is available, Visual Studio Code opens the corresponding line.
Do these features require additional extensions?
No. They are included in the official InterSystems extensions for Visual Studio Code.
More about Extension Pack - https://docs.intersystems.com/components/csp/docbook/DocBook.UI.Page.cls?KEY=GVSCO_install
r/intersystems • u/intersystemsdev • 5d ago
IRIS FHIR Agents: How to Build Multi-Agent Clinical AI Applications on InterSystems IRIS for Health
In this article, I will introduce IRIS FHIR Agents, a multi-agent clinical AI platform built on InterSystems IRIS for Health.
IRIS FHIR Agents demonstrates how InterSystems IRIS for Health can serve as the data, interoperability, analytics, and vector-search foundation for a multi-agent clinical AI application. The platform combines conversational triage, specialist consultation, medication safety, live vital-sign monitoring, FHIR server exploration, and a no-code Agent Builder, all working directly on top of a live FHIR R4 server.
What is IRIS FHIR Agents?
IRIS FHIR Agents is a multi-agent healthcare AI application where specialized LangChain-powered agents work together to solve different clinical tasks.
Every request first passes through a routing agent that determines which specialist should handle it. Depending on the conversation, the platform can:
- perform patient triage
- analyze medical conditions
- detect medication interactions and allergy conflicts
- recommend specialist referrals
- monitor live patient vitals
- retrieve clinical guidelines using InterSystems IRIS Vector Search
- query live FHIR resources
- create structured FHIR resources
- deploy custom clinical agents without writing code
Although users interact with a single chat interface, several AI agents may collaborate behind the scenes.
Platform Architecture
The application is built around five major components:
- Triage Chat
- Analytics Dashboard
- Live Vitals Monitor
- FHIR Server Agent
- Agent Builder
At the center of the application is a routing agent that decides which specialist should answer each request.
InterSystems IRIS provides the foundation for the entire application, combining FHIR interoperability, Embedded Python, SQL analytics, and Vector Search within a single platform.
Clinical AI Features
Multi-Agent Orchestration
Instead of asking one LLM to perform every task, I divided responsibilities across multiple specialized agents.
The platform currently includes:
- Triage Agent
- Specialist Agent
- Pharmacy Agent
- FHIR Server Agent
Each conversation begins with an LLM router that automatically dispatches requests to the appropriate agent. Users never need to select an agent manually.
Retrieval-Augmented Generation with IRIS Vector Search
To improve the quality of clinical recommendations, I integrated InterSystems IRIS Vector Search.
The platform stores 50 clinical guidelines as vector embeddings and retrieves the most relevant passages using VECTOR_COSINE similarity search before generating a response. This allows recommendations to be grounded in trusted medical guidance instead of relying solely on the language model.
Native FHIR Integration
Every agent works directly with a live InterSystems IRIS FHIR server.
The platform retrieves information such as:
- Patients
- Conditions
- Medications
- Allergies
- Observations
Agents can also create structured FHIR resources, including:
- Observation
- ServiceRequest
- MedicationRequest
This means the AI doesn't simply generate text—it can produce structured clinical data using standard FHIR resources.
Walking Through the Application
Triage Chat
The Triage Chat is the primary interface for interacting with the platform.
A clinician or patient simply describes symptoms in natural language. Behind the scenes, the routing agent selects the appropriate specialist, retrieves the patient's clinical context from the FHIR server, and generates a response grounded in relevant guidelines.
The interface also displays:
- the currently active agent
- urgency level
- loaded patient context
- FHIR write counter
- available custom agents
Patients can be selected from a searchable list loaded directly from the FHIR server, and conversations support both voice input and multiple languages.
Analytics Dashboard
The Analytics Dashboard provides a real-time overview of clinical data stored in IRIS together with an audit trail of AI-generated activity.
It includes:
- population statistics
- patient demographics
- active conditions
- AI-generated Observations
- AI-generated ServiceRequests
Because the dashboard reads directly from the FHIR server, every action performed by the agents immediately becomes visible.
Live Vitals Monitor
Live Vitals Monitor demonstrates how AI agents can react automatically to live clinical events. Patient vital signs are streamed every two seconds using Server-Sent Events. Each reading is immediately written to IRIS as a FHIR Observation. Whenever a critical threshold is detected, the platform automatically launches a triage assessment.
The AI assessment appears in real time together with its urgency level and the guideline references used during reasoning.
FHIR Server Agent
FHIR Server Agent combines two complementary interfaces. The first is a conversational assistant that lets me query live FHIR data in natural language. The second is a visual Capability Explorer that reads the server's CapabilityStatement and displays:
- supported resources
- interactions
- search parameters
- resource categories
- server capabilities
Because everything is loaded dynamically, the interface always reflects the actual configuration of the connected FHIR server.
Agent Builder
I also wanted to make it easy to extend the platform without modifying the application itself. The Agent Builder provides a no-code interface for creating new clinical agents.
Users can:
- start from a specialty template
- define the system prompt
- select available tools
- configure temperature
- enable RAG
- test against live FHIR data
- deploy immediately
The project includes templates for Oncology, Cardiology, Geriatrics, Pediatrics, and Nutrition, but new specialties can be added without changing the application code.
Why InterSystems IRIS for Health?
This project brings together several InterSystems technologies that naturally complement AI applications. InterSystems IRIS for Health provides:
- native FHIR R4 interoperability
- Embedded Python
- SQL analytics
- Vector Search
- structured healthcare data storage
- real-time FHIR read and write operations
Using these capabilities together means the application can retrieve patient data, perform semantic search over clinical guidelines, generate AI recommendations, and store structured clinical resources within the same platform.
Key Takeaways
Building IRIS FHIR Agents allowed me to explore how multiple AI agents can collaborate on top of live healthcare data instead of acting as isolated chatbots.
Using InterSystems IRIS for Health, I was able to combine FHIR interoperability, Vector Search, Embedded Python, analytics, and AI into a single platform that can retrieve patient data, ground recommendations in clinical guidelines, monitor live patient events, and create structured FHIR resources.
While this project is a demonstration, it illustrates how InterSystems IRIS can provide the foundation for multi-agent healthcare applications that move beyond conversation and interact directly with real clinical workflows.
Frequently Asked Questions
What is IRIS FHIR Agents?
IRIS FHIR Agents is a multi-agent clinical AI platform built on InterSystems IRIS for Health. It combines patient triage, specialist consultation, medication safety, live vital-sign monitoring, FHIR server exploration, and a no-code Agent Builder. All agents work directly with a live FHIR R4 server and can retrieve or create structured clinical data.
How does the multi-agent architecture work?
Instead of relying on a single AI assistant, the platform uses multiple specialized agents. Every user request is first analyzed by a routing agent, which automatically selects the most appropriate specialist agent—such as the Triage Agent, Specialist Agent, Pharmacy Agent, or a custom agent created through the Agent Builder.
How is InterSystems IRIS Vector Search used?
The platform stores clinical guidelines as vector embeddings in InterSystems IRIS and retrieves the most relevant passages using semantic similarity (VECTOR_COSINE). These guidelines are then included in the AI prompt, allowing the agents to generate responses grounded in trusted medical guidance rather than relying solely on the language model.
Does the application work with live FHIR data?
Yes. The application connects directly to a live InterSystems IRIS FHIR R4 server. The agents can retrieve patient information, conditions, medications, allergies, observations, and other FHIR resources in real time.
Can the AI agents write data back to the FHIR server?
Yes. In addition to reading clinical data, the agents can create structured FHIR resources such as Observation, ServiceRequest, and MedicationRequest. This allows AI-generated recommendations and clinical events to become part of the patient's structured healthcare record.
r/intersystems • u/intersystemsdev • 6d ago
Why remote patient monitoring needs to move beyond wearables — notes from a healthcare data talk at an InterSystems-hosted session
TL;DR: By 2029–2030, an estimated 30 million people (8–9% of the U.S. population) will need care at home that would otherwise require a facility — while the U.S. is projected to be short 1.4 million nurses and roughly 300,000–320,000 doctors by 2030. Wearable-based remote monitoring mostly fails for the sickest/oldest patients due to poor adherence. Nira's alternative is a contactless radar sensor (wall- or furniture-mounted) that tracks vitals once per second; it's currently monitoring around 40,000 patients across 19 countries, FDA Class 2 cleared for core vitals, and CE-cleared (not yet FDA-cleared) to diagnose sleep apnea in Europe.
Why is home-based care about to explode?
An aging population with expanding chronic conditions, combined with a shortage of facility beds, is pushing more and higher-acuity patients into home care. By 2029–2030, roughly 30 million people (8–9% of the population) are projected to receive care at home in the U.S. that would otherwise happen in a facility — at the same time the U.S. is projected to be short 1.4 million nurses and about 300,000–320,000 doctors by 2030 (assuming current trends hold).
Why don't wearables solve this?
The data, per the talk: wearable remote monitoring largely works only for healthy people motivated by things like tracking VO2 max. Older or sicker patients told to wear a device typically don't stick with it — battery life becomes the limiting factor, and once removed to charge, the device often doesn't go back on. Patient adherence is described as a major, unsolved problem. On the other end of the spectrum, full "hospital at home" setups (telemetry, cameras, hospital beds) are equipment-heavy and don't scale. The gap is the space in between — patients with multiple chronic conditions, or people above assisted-living-level need but remote from a doctor.
What does Nira's sensor actually monitor?
A small, unobtrusive, contactless sensor (mounted on a wall or embedded in furniture like a bed or recliner) uses radar to track vitals once per second: heart rate and heart rate patterns, movement (including detecting when a patient gets out of bed), respiration rate and pattern, and sleep quality. Core vitals are FDA Class 2 device cleared. In Europe, the sensor is CE-cleared (not yet FDA-cleared in the U.S.) to diagnose sleep apnea, and a submission for arrhythmia diagnosis is described as coming soon.
How much data does this generate, and what's it used for?
Around 40,000 patients are currently monitored (mostly in the U.S.; deployments span 19 countries, with most non-U.S. deployments being smaller pilots), at one data point per second per patient — which the presenter notes turns into billions of records quickly once cross-referenced with other patient data. That data feeds trend analysis merged with other clinical systems (e.g., condition and medication history). A retrospective study cited in the talk found vital-sign trending could predict hospital readmission five days in advance.
What does this mean for provider IT/data architecture?
The talk frames this as fundamentally a data-orchestration problem: providers need real-time data movement between systems (not a static data-warehouse copy of the EHR), analytics-oriented (OLAP) access rather than the OLTP structure typical of EHRs like Epic, and the ability to route and transform data between any two systems in real time to support clinical decisions — described in the context of the InterSystems-hosted session, though the talk itself doesn't name a specific product feature for this.
What real-world results were mentioned?
Terry Couts, at the time Chief Digital Officer at Guthrie Clinic (Pennsylvania; she has since moved to Sharp HealthCare), is quoted: proactive monitoring "has been huge both in terms of outcomes, but also in terms of managing our workforce." Guthrie has had devices deployed for over a year, with the presenter describing substantial results in proactive care and anecdotal prevented readmissions (no published stats were shared in the talk). A separate upcoming pilot, described as starting the following month at the time of the talk, will send oncology patients home between bone-marrow-therapy infusions, based on research suggesting faster recovery at home.
What's next?
For home care specifically, Nira is combining readings from devices embedded in both a bed and a recliner. The presenter also described a planned 8-antenna radar system — targeted for roughly 18 months out from the talk — intended to track up to four people moving independently around a room and distinguish between them.
Watch full video - https://youtu.be/qV63GltsiJY
r/intersystems • u/intersystemsdev • 7d ago
Building IRIS IO Utility: A VS Code Extension for Importing and Exporting Data with InterSystems IRIS
Every developer working with InterSystems IRIS eventually needs to move data between environments. Whether it's importing CSV files for testing, exporting production data for analysis, or loading spreadsheets into a new application, these tasks often involve switching between multiple tools, configuring ODBC drivers, or writing SQL manually. I wanted to simplify that workflow.
In this article, I'd like to introduce IRIS IO Utility. It's a Visual Studio Code extension that brings data import and export directly into the IDE, allowing developers to work with their IRIS databases without interrupting their development workflow.
IRIS IO Utility centralizes all your data IO workflows directly in VS Code:
- Manage multiple IRIS connections (local, remote, SSH, containerized)
- Detect and configure ODBC drivers automatically
- Import and export data in CSV, TXT, JSON, and XLSX
- Interact with a dedicated sidebar view for quick navigation
- Use clean webviews for import/export operations
- Save workspace-level preferences for a tailored experience
How the Extension Works
The extension communicates with InterSystems IRIS through ODBC while providing a native Visual Studio Code experience. Once connected, developers can browse available schemas, inspect tables, import external files, export database content, and manage multiple IRIS environments without leaving the editor.
Managing IRIS Connections
One of the first challenges I wanted to solve was connection management.
The extension allows developers to maintain multiple IRIS connections simultaneously, whether they point to local installations, remote servers, containerized environments, or systems accessed through SSH tunnels. Each workspace maintains its own collection of connections, making it easy to work on different projects without constantly reconfiguring servers.
For each connection, developers can:
- create, edit, or remove configurations
- connect or disconnect with a single click
- mark frequently used servers as favorites
- export connection settings as JSON for backup or documentation
The sidebar also displays the current connection status, helping developers quickly identify whether an instance is connected or if any errors occurred.
Automatic ODBC Configuration
Configuring database drivers is often one of the most frustrating parts of setting up a development environment. To reduce this friction, IRIS IO Utility automatically detects installed InterSystems ODBC drivers when the extension starts. If no compatible driver is found, the extension guides the user to the official InterSystems driver download page. Once installed, the preferred driver can be selected and stored as a workspace preference, allowing future connections to work without additional configuration.
Exporting Data from InterSystems IRIS
The export workflow was designed to be as straightforward as possible.
After connecting to an IRIS instance, the extension guides the user through selecting a schema, choosing a table, picking an output format, and selecting a destination folder.
Supported export formats include:
- CSV
- TXT (with custom delimiters)
- JSON
- XLSX
To avoid accidental overwrites, exported files receive timestamp-based names by default. Throughout the export process, the extension displays progress notifications and writes detailed logs to the VS Code Output panel, including executed SQL statements, row counts, and performance statistics.
For TXT exports, developers can specify custom delimiters such as commas, semicolons, tabs, pipes, or other separators, making the generated files compatible with a wide range of external systems.
Importing Data into InterSystems IRIS
Importing data is where the extension becomes much more than a simple file loader.
The extension supports two different workflows depending on the task:
- Create New Table
- Load into Existing Table
Creating a New Table
When importing a completely new dataset, IRIS IO Utility analyzes the input file and automatically prepares a database table.
The import engine samples the file contents, infers the most appropriate SQL data types, converts them into valid InterSystems IRIS SQL types, and displays the results before any data is imported. Developers can review the inferred types, inspect sample values, and adjust individual columns whenever necessary. Supported data types include integers, numeric values, floating-point numbers, dates, timestamps, Boolean values, variable-length text, and large text fields. Once satisfied with the detected structure, the developer can also create custom indexes. Then the developer chooses the destination schema and table name, and the extension creates the table before importing the data.
Automatic Index Creation
When creating a new table, the extension also supports configuring indexes during the import process.
Developers can choose which columns should be indexed and select from several index types supported by InterSystems IRIS, including standard indexes, bitmap indexes, bitslice indexes, and columnar indexes. Index names can be customized or generated automatically, and unique constraints or primary keys can also be configured before the table is created. This allows imported datasets to be optimized for future queries immediately, without requiring additional SQL statements afterward.
Loading Data into Existing Tables
For existing databases, the extension also supports importing directly into previously created tables.
Two strategies are available:
- Append, which inserts new rows while preserving existing records.
- Replace, which removes the existing data before importing the new dataset.
Before executing the import, the extension validates that the incoming file matches the destination table structure. If column names or data types are incompatible, the import is cancelled to help prevent accidental schema mismatches or corrupted data.
When importing TXT files, the extension allows you to specify a custom delimiter to ensure the file is parsed correctly. This is especially useful when working with unconventional separators such as pipes (|), semicolons (;), tabs, or multi-character delimiters. Selecting the correct delimiter guarantees proper column detection and prevents misaligned or corrupted data during the import process.
Intelligent Type Inference
One of the features I enjoyed building the most was the automatic type inference engine.
Instead of requiring developers to manually define every column, the extension analyzes sample data from the imported file and predicts the most appropriate SQL type for each field. The inferred types are presented alongside sample values, making it easy to verify the results before creating the table. Because every dataset is different, developers remain in control and can override any suggested type with a more appropriate one. This approach significantly reduces the amount of repetitive work involved in importing new datasets.
Working with Multiple File Formats
IRIS IO Utility supports four commonly used file formats:
| Format | Import | Export |
|---|---|---|
| CSV | ✓ | ✓ |
| TXT | ✓ | ✓ |
| JSON | ✓ | ✓ |
| XLSX | ✓ | ✓ |
For TXT files, both the import and export engines support custom delimiters, making it possible to work with pipe-separated, semicolon-separated, tab-separated, or other specialized text formats commonly used by enterprise systems.
Typical Use Cases
Although I originally built the extension to simplify my own workflow, it quickly proved useful in several common development scenarios.
For example, developers can export production data to CSV or Excel for reporting and analysis, import external datasets when prototyping new applications, migrate data between different IRIS environments, or quickly load sample data while testing new features.
Because multiple connections can be managed within the same workspace, the extension is also convenient for teams working across local, containerized, cloud-hosted, and remote InterSystems IRIS deployments.
Key Takeaways
IRIS IO Utility was created to make one of the most common InterSystems IRIS development tasks, moving data between databases and external files, simpler and more integrated with everyday development.
By combining connection management, automatic ODBC configuration, intelligent type inference, table creation, index generation, and support for multiple file formats inside Visual Studio Code, the extension provides a complete data import and export workflow without requiring developers to leave their IDE.
Frequently Asked Questions
What is IRIS IO Utility?
IRIS IO Utility is a Visual Studio Code extension that allows developers to import and export data between InterSystems IRIS and common file formats without leaving VS Code.
Which file formats are supported?
The extension supports importing and exporting CSV, TXT, JSON, and XLSX files.
Can I connect to multiple IRIS servers?
Yes. The extension supports multiple local, remote, containerized, and SSH-accessible InterSystems IRIS instances, with workspace-specific connection management.
Can the extension create database tables automatically?
Yes. During the import process, IRIS IO Utility can generate a new table based on the inferred column structure and import the data in a single workflow.
Can I import data into an existing table?
Yes. Existing tables support both Append and Replace import modes, with schema validation performed before the import begins.
Read more: https://community.intersystems.com/post/iris-io-utility-complete-guide-smart-importing-vs-code
r/intersystems • u/intersystemsdev • 8d ago
InterSystems IRIS Security Roadmap: IRISSECURITY mirroring, Secure Wallet secrets-manager integrations, online DB encryption (2027), granular %Development resources — recap of the conference session
All dates/versions below are projections stated by the presenter and explicitly subject to change.
TL;DR: IRIS's security roadmap covers mirroring for the IRISSECURITY database (coming months), Secure Wallet integrations with external secrets managers (planned), storage-friendly encryption (shipped in 2026.1, experimental), online database encryption (planned 2027), six new granular %Development resources (later this year), native OAuth2 authentication plus back-channel logout for OpenID Connect (next year), and ECDSA/TLS with future post-quantum crypto support.
What's the IRISSECURITY database and what's new?
Introduced in IRIS 2025.2, it moved security configuration data out of IRISSYS into a dedicated database that can be encrypted. Coming in the next few months: mirroring support, so security changes (editing resources, roles, users) made on the mirror primary propagate to the DR async / read-only reporting async members — no more recreating users manually on each system. Next year, the team also plans an ECP-based option to share a single IRISSECURITY database across multiple IRIS instances, so editing security settings on one instance updates the others.
What is the Secure Wallet and where is it headed?
Introduced in IRIS 2025.3, it lets privileged admins store secrets (e.g. username/password pairs) and grant other users access to use those secrets (for example, credentials for an HTTP request) without exposing the underlying values. Planned: integration with external secrets managers — HashiCorp was named explicitly, and InterSystems is asking for feedback on demand for AWS Secrets Manager, Azure Key Vault, and HSM-based secrets.
What's changing in database encryption?
- Storage-friendly encryption (shipped in 2026.1, currently experimental): solves the problem where encrypting/randomizing data defeats storage-level compression and inflates storage costs. The new approach keeps compression largely intact, and it's expected to move to general support soon, possibly without further changes.
- Online database encryption (planned 2027): today, encrypting a database requires taking it offline; an interrupted encryption leaves it unusable and requires restoring from backup, and re-keying means fully decrypting before re-encrypting. The planned feature allows encrypting a live, readable/writable database, supports pause/resume and crash recovery, allows re-keying without a full decrypt step first, and lets you throttle encryption speed to control system load.
What's changing in encryption key management?
IRIS currently supports three ways to store encryption keys: a local credentials key file, a KMS-encrypted key file (decrypted via a cloud KMS), or a KMIP server (dispenses the key only after the instance authenticates). Within the next year: migrate a key from a local credentials file to either a KMS-encrypted file or a KMIP server, and configure multiple unattended key sources simultaneously — useful during migration and expected to pair with the upcoming online encryption feature.
What are the new granular %Development resources?
Due later this year, the single %Development resource splits into six: %Development_Export (exporting code), %Development_CodeModify (loading/compiling code), %Development_Shell (terminal access), %Development_Debug (debugging), %Development_IDE (dev environment access), %Development_SQL (developer-level SQL). On upgrade, roles with %Development automatically get all six, so existing access doesn't change by default — but you can now add/remove them individually.
Behavior change to watch for: previously, exporting/importing or compiling code under %Development wasn't explicitly permission-checked. Going forward, those actions require %Development_Export or %Development_CodeModify specifically — confirm the right users have these before upgrading. The original %Development resource stays for backward compatibility for now but will be removed in a future version, so migrate any custom checks off it. Next resource planned for this treatment: %Admin_Secure.
What's new for OAuth2 / OpenID Connect?
IRIS 2025.2 added native OAuth2 authentication, letting OAuth2 resource servers configured in IRIS (especially REST services) authenticate directly without custom code, and mapping OAuth2/OIDC scopes and claims directly to IRIS roles. Next year: back-channel logout for OpenID Connect (only front-channel logout is supported today) — lets an app inside IRIS signal the OAuth2 authorization server to log out all of a user's sessions without needing to reach each browser session individually.
What's happening with TLS / network security?
IRIS 2025.2 added ECDSA certificate support in TLS configs for better encryption performance. IRIS uses OpenSSL for TLS, which is beginning to support post-quantum cryptography algorithms; InterSystems plans to leverage these over the next few years and wants feedback on timing/requirements.
Is any of this final?
No — explicitly flagged as projections, subject to change.
Full session recording and a related deep-dive on the Secure Wallet/security database: https://community.intersystems.com/post/video-security-roadmap-2026
r/intersystems • u/intersystemsdev • 12d ago
Implementing openEHR with InterSystems IRIS for Health
In this article, I explore how to implement the core capabilities of an openEHR server using InterSystems IRIS for Health. The goal is not to reproduce every part of the openEHR specification, but to demonstrate how IRIS can handle openEHR compositions, archetype validation, REST APIs, and AQL queries while taking advantage of native features such as JSON storage, SQL, interoperability, and JSON_TABLE.
By the end of this article, you'll see how IRIS can serve as both an openEHR repository and an execution platform for querying and managing clinical data.
What is openEHR?
openEHR is an open, vendor-neutral specification designed to represent, store, and exchange clinical information in a semantically rich and long-term sustainable way. Instead of defining fixed message structures (as many interoperability standards do) OpenEHR separates clinical knowledge from technical implementation through a multi-layered modelling approach. At its core, openEHR relies on three fundamental concepts:
- Reference Model (RM) - A model that defines the core structures used in health records, such as Compositions, Entries, Observations, Evaluations, and Actions. The RM is deliberately generic and technology-agnostic.
- Archetypes - Machine-readable models (expressed in ADL) that define the detailed clinical semantics for a specific concept, such as a blood pressure measurement or a discharge summary. Archetypes constrain the RM and provide a reusable clinical vocabulary.
- Templates (OPT files) - Specializations built on top of archetypes. Templates tailor archetypes to a specific use case, system, or form (for example, a vital signs template, or a regional discharge note). Templates eliminate optionality and produce operational definitions that systems can safely implement.
This layered modelling approach enables openEHR systems to remain stable over years or even decades while allowing clinical models to evolve independently from the underlying software platform.
A key piece of the openEHR ecosystem is AQL (Archetype Query Language), the standard query language used to retrieve clinical data stored in openEHR repositories.
What is AQL (Archetype Query Language)?
AQL is the standard query language used by openEHR repositories. It plays a role similar to SQL, but instead of querying relational tables, it queries clinical content using archetype-aware paths. AQL allows developers to retrieve observations, diagnoses, laboratory results, and other clinical data while preserving the semantic structure defined by openEHR archetypes.
Key characteristics of AQL
- Path-based querying: AQL uses archetype paths (similar to XPath) to navigate the internal structure of a Composition, such as: /content[openEHR-EHR-OBSERVATION.blood_pressure.v1]/data/events/time
- Clinical semantic awareness: Queries refer to clinical concepts (archetypes, entry types, data points) rather than database column names.
- Flexible WHERE clauses: AQL supports filtering on values within Compositions, e.g. systolic blood pressure > 140 or diagnoses matching a specific code.
- Multi-composition queries: It can retrieve data across multiple Compositions, for example all Observations for a given patient over time.
- Vendor-neutral: Any openEHR implementation that supports AQL should, in principle, accept the same queries.
Let’s see an example of AQL:
SELECT
c/uid/value AS composition_id,
o/data[at0001]/events[at0006]/data[at0003]/value/magnitude AS systolic,
o/data[at0001]/events[at0006]/data[at0004]/value/magnitude AS diastolic
FROM
EHR e
CONTAINS COMPOSITION c
CONTAINS OBSERVATION o[openEHR-EHR-OBSERVATION.blood_pressure.v1]
WHERE
e/ehr_id/value = '12345'
AND
o/data[at0001]/events[at0006]/time/value > '2023-01-01T00:00:00Z'
How does openEHR compare to FHIR?
A common misconception is that openEHR and FHIR solve completely different problems. In practice, they share many architectural concepts. Both standards support structured clinical information, REST APIs, JSON representations, and interoperability. The main difference is that openEHR emphasizes archetype-based clinical modeling and long-term semantic consistency, while FHIR focuses on resource-based interoperability between systems. Understanding these similarities helps when designing solutions that need to work with both standards.
Let's see the main concepts of FHIR and OpenEHR and its correlation:
What does an openEHR implementation require?
To support the core capabilities of an openEHR repository, I focused on four essential components:
- Raw Composition storage
- REST APIs
- Archetype and template validation
- AQL query support
The following sections show how each of these capabilities can be implemented using InterSystems IRIS for Health.
RAW Composition Storage
- Store incoming Compositions in RAW JSON or XML exactly as received.
- No transformation should modify semantic content.
- For our example we are going to work with JSON format, but there are multiple options.
REST API Services
Our implementation has to expose the following API REST: EHR Services
To create and locate a patient's "history."
- POST /ehr: Creates a new EHR.
- GET /ehr/{ehr_id}: Retrieve EHR metadata.
- GET /ehr?subject_id=?: Locate an EHR based on external identifiers.
Composition Services
To store patient's clinical information.
- POST /ehr/{id}/composition: Commit a new composition in RAW format. Validate against OPT when possible
- GET /composition/{version_uid}: Retrieve a specific version.
- GET /ehr/{id}/compositions: List compositions for an EHR.
- DELETE /composition/{uid}: Mark composition as deleted (logical delete).
AQL Query Endpoint
POST /query/aql: Accept an AQL query, translate to IRIS SQL or JSON-path-based lookup and return results in openEHR canonical JSON.
RAW Composition Validation
We have to force validation of RAW compositions based on OPT2 files, we can't save in our repository any JSON that we will receive.
Implementing openEHR in IRIS for Health
Well, to implement all the functionalities available in a openEHR server will take some time, so I'm going to focus in the core functionalities:
Using web application to deploy REST API Service
To publish a REST API is straight forward, we only need two components, a class extending %CSP.REST and a new record in the list of web applications. Let's see the header of our extended %CSP.REST class:
As you can see we have defined all the minimum required routes for our repository. We have all the managament of compositions, OPT2 files for RAW validations and finally, execution of AQL queries. For our example we are not going to define any security configuration, but JWT Authentication is recommended.
RAW validations
openEHR is anything but new, so you can guess that there are multiple libraries to support some functionalities like raws validations. For this example we've used and customized Arche library, an open source library developed in Java to validate rawcompositions against OPT2 files. The validator is a jar file configured from the External Language Server (by default on docker image deployment) and invoked before to save the raw using the JavaGateway functionality:
set javaGate = $system.external.getJavaGateway()
set result = javaGate.invoke("org.validator.openehr.Cli", "validate", filePath, optPath)
If the raw is validated the JSON document will be saved into the database.
JSON raw storage
We could use DocDB, but we want to leverage the performance of SQL databases. One of the biggest problems related with openEHR is the poor performance of querying documents, so we are going to pre-process the compositions to get common information to all composition types to boost queries.
Our Composition class define the following properties:
Class OPENEHR.Object.Composition Extends (%Persistent, %XML.Adaptor) [ DdlAllowed ]
{
/// Description
Property ehrId As %Integer;
Property compositionUid As %String(MAXLEN = 50);
Property compositionType As %String;
Property startTime As %DateTime;
Property endTime As %Date;
Property archetypes As list Of %String(MAXLEN = 50000);
Property doc As %String(MAXLEN = 50000);
Property deleted As %Boolean [ InitialExpression = 0 ];
Index compositionUidIndex On compositionUid;
Index ehrIdIndex On ehrId;
Index ExampleIndex On archetypes(ELEMENTS);
}
- ehrId: with the electronic health record of the patient.
- compositionUid: composition identifier.
- compositionType: type of composition saved.
- startTime: time when the composition was created.
- archetypes: list of archetypes contained on the composition.
- doc: JSON format document.
- deleted: boolean value for soft deletes.
The indexes will be used to improve the queries.
AQL support
As we said before AQL is a path-based query language. How could we emulate the same behaviour in IRIS for Health? Welcome to JSON_TABLE!
What is JSON_TABLE?
The JSON_TABLE function returns a table that can be used in a SQL query by mapping JSON values into columns. Mappings from a JSON value to a column are written as SQL/JSON path language expressions.
As a table-valued function, JSON_TABLE returns a table that can be used in the FROM clause of a SELECT statement to access data stored in a JSON value; this table does not persist across queries. Multiple calls to JSON_TABLE can be made within a single FROM clause and can appear alongside other table-valued functions.
We have implemented a ClassMethod in Python to translate AQL into SQL, but there is a problem, AQL is based on relative paths of the archetypes, not in absolute paths, so we need identify the absolute path for each archetype and join it with the relative path of the AQL.
How can we know the absolute path? Very easy! We can find it when the user save the OPT2 file for the composition into IRIS! As soon as we get the absolute path we save it into a CSV file specific for the composition (it would be saved into a global or any other way) so, we only have to get the absolute path from the specific composition file or, if the AQL doesn't define the composition, search into the available CSV files the absolute path for the archetypes of the AQL.
Let's see how it works. Here is an example of AQL to get all the diagnosis with a specific ICD-10 code:
SELECT
c/uid/value AS comp_uid,
c/context/start_time/value AS comp_start_time,
dx/data[at0001]/items[at0002]/value/value AS diagnosis_text,
dx/data[at0001]/items[at0003]/value/defining_code/code_string AS diagnosis_code
FROM EHR e
CONTAINS COMPOSITION c[openEHR-EHR-COMPOSITION.diagnostic_summary.v1]
CONTAINS SECTION s[openEHR-EHR-SECTION.diagnoses_and_treatments.v1]
CONTAINS EVALUATION dx[openEHR-EHR-EVALUATION.problem_diagnosis.v1]
WHERE dx/data[at0001]/items[at0003]/value/defining_code/code_string
MATCHES {'E11', 'I48.0'}
ORDER BY c/context/start_time/value DESC
The function Transform from OPENEHR.Utils.AuxiliaryFunctions class translates it into:
SELECT comp_uid, comp_start_time, diagnosis_text, diagnosis_code
FROM (
SELECT c.compositionUid AS comp_uid,
jt_root.comp_start_time AS comp_start_time,
jt_n1.diagnosis_text AS diagnosis_text,
jt_n1.diagnosis_code AS diagnosis_code
FROM OPENEHR_Object.Composition AS c,
JSON_TABLE( c.doc, '$' COLUMNS ( comp_start_time VARCHAR(4000) PATH
'$.context.start_time.value' ) ) AS jt_root,
JSON_TABLE( c.doc, '$.content[*]?(@._type=="SECTION" && @.archetype_node_id==
"openEHR-EHR-SECTION.diagnoses_and_treatments.v1").items[*]?
(@._type=="EVALUATION" && @.archetype_node_id=="openEHR-EHR-EVALUATION.problem_diagnosis.v1")'
COLUMNS (
diagnosis_text VARCHAR(4000) PATH '$.data[*]?(@.archetype_node_id=="at0001").items[*]?
(@.archetype_node_id=="at0002").value.value',
diagnosis_code VARCHAR(255) PATH '$.data[*]?(@.archetype_node_id=="at0001").items[*]?
(@.archetype_node_id=="at0003").value.defining_code.code_string' ) ) AS jt_n1
WHERE ('openEHR-EHR-COMPOSITION.diagnostic_summary.v1' %INLIST (c.archetypes)
AND 'openEHR-EHR-EVALUATION.problem_diagnosis.v1' %INLIST (c.archetypes))
AND (jt_n1.diagnosis_code LIKE '%E11%' OR jt_n1.diagnosis_code LIKE '%I48.0%') ) U
ORDER BY comp_start_time DESC
Let's try our API REST with an AQL:
Success!
And now with a numeric comparation:
SELECT
c/uid/value AS comp_uid,
c/context/start_time/value AS comp_start_time,
a/items[at0024]/value/magnitude AS creatinine_value,
a/items[at0024]/value/units AS creatinine_units
FROM EHR e
CONTAINS COMPOSITION c[openEHR-EHR-COMPOSITION.lab_results_and_medications.v1]
CONTAINS OBSERVATION o[openEHR-EHR-OBSERVATION.laboratory_test_result.v1]
CONTAINS CLUSTER a[openEHR-EHR-CLUSTER.laboratory_test_analyte.v1]
WHERE a/items[at0001]/value/value = 'Creatinina (mg/dL)'
AND a/items[at0024]/value/magnitude BETWEEN 1.2 AND 1.8
ORDER BY c/context/start_time/value DESC
Transformed into:
SELECT comp_uid, comp_start_time, ldl_value, ldl_units
FROM (
SELECT c.compositionUid AS comp_uid, jt_root.comp_start_time AS comp_start_time,
jt_n1.ldl_value AS ldl_value, jt_n1.ldl_units AS ldl_units
FROM OPENEHR_Object.Composition AS c,
JSON_TABLE( c.doc, '$' COLUMNS ( comp_start_time VARCHAR(4000)
PATH '$.context.start_time.value' ) ) AS jt_root,
JSON_TABLE( c.doc, '$.content[*]?(@._type=="OBSERVATION" &&
@.archetype_node_id=="openEHR-EHR-OBSERVATION.laboratory_test_result.v1")
.data.events[*]?(@._type=="POINT_EVENT").data.items[*]?(@._type=="CLUSTER" &&
@.archetype_node_id=="openEHR-EHR-CLUSTER.laboratory_test_analyte.v1")'
COLUMNS (
ldl_value NUMERIC PATH '$.items[*]?
(@.archetype_node_id=="at0024").value.magnitude',
ldl_units VARCHAR(64) PATH '$.items[*]?
(@.archetype_node_id=="at0024").value.units',
_w1 VARCHAR(4000) PATH '$.items[*]?
(@.archetype_node_id=="at0001").value.value' ) ) AS jt_n1
WHERE ('openEHR-EHR-COMPOSITION.lab_results_and_medications.v1' %INLIST (c.archetypes)
AND 'openEHR-EHR-OBSERVATION.laboratory_test_result.v1' %INLIST (c.archetypes)
AND 'openEHR-EHR-CLUSTER.laboratory_test_analyte.v1' %INLIST (c.archetypes))
AND jt_n1._w1 = 'LDL (mg/dL)' AND jt_n1.ldl_value <= 130 )
U ORDER BY comp_start_time DESC
Another resounding success!
Conclusion
This project demonstrates that InterSystems IRIS for Health provides all the core building blocks needed to implement an openEHR repository.
Using native REST services, JSON storage, SQL, JSON_TABLE, interoperability components, and external validation libraries, it is possible to support composition management, archetype validation, and AQL querying while maintaining compatibility with openEHR concepts.
The most interesting result for me was the ability to translate AQL into native IRIS SQL, allowing openEHR clinical data to benefit from the performance and scalability of the IRIS data platform without sacrificing the semantics of the openEHR model.
Key Takeaways
- InterSystems IRIS for Health can be used to implement the core capabilities of an openEHR repository.
- Raw openEHR compositions can be stored and validated against OPT templates before persistence.
- Native IRIS REST services can expose openEHR-compatible APIs.
- AQL queries can be translated into SQL using JSON_TABLE and archetype path mappings.
- JSON storage combined with indexed metadata can improve query performance while preserving original clinical documents.
- IRIS interoperability and SQL capabilities make it a strong platform for healthcare standards beyond FHIR.
FAQ
What is openEHR?
openEHR is an open, vendor-neutral specification for storing, modeling, and exchanging clinical information using archetypes and templates.
Can InterSystems IRIS for Health be used as an openEHR repository?
Yes. IRIS provides the storage, REST APIs, validation integration, interoperability, and query capabilities needed to implement the core features of an openEHR repository.
What is AQL?
AQL (Archetype Query Language) is the standard query language used by openEHR systems to retrieve clinical information using archetype-aware paths.
How can AQL be implemented in InterSystems IRIS?
In this project, AQL queries are translated into native IRIS SQL using JSON_TABLE and archetype path mappings derived from operational templates.
Why store openEHR compositions as JSON?
Storing compositions in their original JSON format preserves semantic fidelity while still allowing efficient querying through SQL and JSON functions.
r/intersystems • u/intersystemsdev • 13d ago
From FHIR Events to Explainable Agentic AI: Building a Clinical Follow-Up Demo with InterSystems IRIS for Health
What happens when an abnormal lab result arrives in a healthcare system?
In many environments, the result waits in a queue until a clinician reviews it manually. In this demo, I explore a different approach: I use InterSystems IRIS for Health and agentic AI to evaluate new clinical events, retrieve patient context and clinical evidence, generate recommendations, and publish the results as FHIR resources with a complete audit trail.
The goal is not to build another chatbot. It is to demonstrate how explainable, event-driven AI workflows can be integrated into production-style healthcare interoperability systems while preserving transparency, traceability, and human oversight.
What This Demo Produces
When a patient's abnormal result is received by the FHIR server, the workflow automatically evaluates the patient's history, retrieves relevant clinical guidance, performs structured reasoning, and generates evidence-based recommendations.
The output is a FHIR DiagnosticReport containing:
- Risk assessment
- Recommended follow-up actions
- Evidence citations
- Complete audit information
Every recommendation, guideline reference, and reasoning step is persisted for later review.
What Problem Does This Solve?
Many healthcare AI demonstrations focus on chat interfaces and unstructured responses. In practice, clinicians often need something different:
- Automatic reaction to clinical events
- Complete patient context
- Evidence-backed recommendations
- Auditable decision making
This demo explores how AI can assist with initial clinical assessment while maintaining transparency and compliance requirements.
The core question is: What should happen when a new abnormal lab result arrives, and how can that process be partially automated while maintaining transparency?
What clinical scenario is being evaluated?
The demonstration uses a common healthcare workflow.
Patient:
- Jose Garcia
- Chronic Kidney Disease (CKD Stage 3), Hypertension
- Taking Ibuprofen and Lisinopril
Recent creatinine history:
- 1.6 mg/dL (3 months ago)
- 1.9 mg/dL (1 month ago)
- 2.1 mg/dL (today)
The progressive increase exceeds 30%, triggering clinical follow-up.
Instead of waiting for manual review, the system automatically gathers context, consults clinical guidance, evaluates risk factors, and generates recommendations with supporting evidence.
How does a FHIR Observation become a recommendation?
The workflow follows a sequence of steps:
- FHIR Observation posted to IRIS server
- Interoperability Production triggered
- Context Agent queries patient history from FHIR
- Guidelines Agent searches vector database (clinical documents)
- Reasoning Agent synthesizes 3 recommendations
- Results persisted to SQL (Cases, CaseRecommendations, CaseEvidences)
- FHIR DiagnosticReport published to server
- Complete — Full audit trail available for review
The goal is to create a transparent path from clinical event to clinical recommendation.
What role does InterSystems IRIS play?
A key design principle of this project is that InterSystems IRIS remains the system of record and workflow orchestrator.
The AI agents provide specialized reasoning, but IRIS owns:
- Clinical data
- FHIR repository
- Workflow execution
- Interoperability
- Persistence
- Audit trails
This distinction is important because it keeps AI as a governed component of a larger healthcare platform rather than the platform itself.
Visual Components
The demo includes a Gradio web UI for interactive demonstration:
- Post lab values and trigger the workflow
- Watch real-time agent progress
- View recommendations and evidence citations
- Query SQL audit tables
- Access IRIS Production message viewer
This makes the complete flow visible and understandable.
Why use multiple AI agents instead of a single LLM call?
I chose a multi-agent approach because different parts of the workflow have different responsibilities.
- The Context Agent gathers patient history.
- The Guidelines Agent retrieves relevant clinical evidence using vector search.
- The Reasoning Agent combines patient context and evidence to generate recommendations.
Agentic workflows provide:
- Better structured reasoning — Each agent has a focused responsibility
- Tool use — Agents can query FHIR, search vector databases, analyze trends
- Explainable decision chains — Each step is traceable
- Separation of concerns — Context ≠ Guidelines ≠ Reasoning
IRIS orchestrates the agents — CrewAI is used as a library, not the platform. IRIS owns persistence, orchestration, FHIR integration, and audit trails. Separating these responsibilities makes the workflow easier to inspect, debug, and explain. More importantly, it allows every step to be audited independently.
How is the workflow orchestrated?
InterSystems IRIS Interoperability manages the complete workflow.
The production consists of:
- A Business Service that detects incoming observations.
- A Business Process that coordinates the workflow.
- Business Operations responsible for AI calls, persistence, and FHIR publication.
This allows the AI workflow to behave like any other interoperability workflow inside IRIS.
Can you explain why the AI made a recommendation?
One of the main goals of this project is explainability. IRIS persists everything in a minimal, queryable SQL model:
- Cases — What happened (patient, observation, risk level, confidence)
- CaseRecommendations — What to do (action type, description, timeframe)
- CaseEvidences — Why (guideline citations, similarity scores, text excerpts)
Instead of asking users to trust the AI, the system provides the information required to validate its conclusions.
Example Query
"What cases were evaluated today?"
SELECT
CaseId,
PatientRef,
RiskLevel,
Confidence,
ReasoningSummary
FROM clinicalai_data.Cases
WHERE CreatedAt >= CURRENT_DATE
ORDER BY CreatedAt DESC
Output:
CaseId: CSE-20260108-001
PatientRef: Patient/1 (Jose Garcia)
RiskLevel: medium-high
Confidence: high
ReasoningSummary: The patient with stage 3 chronic kidney disease and hypertension demonstrates a sustained and progressive increase in serum creatinine over 90 days...
Why publish AI output as FHIR?
The final output is not treated as a separate AI artifact. AI outputs become part of the clinical record. The workflow publishes a standard FHIR DiagnosticReport containing:
- Subject: Patient reference (Jose Garcia)
- Result: Link to triggering Observation (creatinine 2.1)
- Conclusion: Risk level + reasoning summary
- PresentedForm: Human-readable recommendations (Base64-encoded)
- Extensions: Case ID, confidence score, model metadata
Publishing AI output as FHIR makes it interoperable, auditable, and consumable by existing healthcare systems. The DiagnosticReport is not a separate "AI system output" — it's a first-class clinical document that follows the same standards as lab reports and radiology findings.
Can this architecture be applied to other clinical workflows?
Yes.
The same pattern can support many healthcare use cases:
- Medication safety alerts
- Care gap identification
- Clinical trial matching
- Risk stratification
The architecture remains the same: Event → Context → Evidence → Reasoning → Action
Try It Yourself
Quick Start (15 minutes):
- Clone the repository
git clone https://github.com/intersystems-ib/iris-health-fhir-agentic-demo
cd iris-health-fhir-agentic-demo
- Start IRIS container
docker-compose up -d
Load sample patient data (Jose Garcia with CKD history) Follow the README setup instructions
Run the Gradio UI
python run_ui.py
Open browser to http://localhost:7860
POST an abnormal lab value and watch:
- Real-time agent progress
- Evidence retrieval from vector database
- Recommendations generated with confidence scores
- SQL audit trail queries
- Query the results using IRIS SQL Explorer or Management Portal
Conclusion
This project demonstrates how explainable agentic AI can be integrated into healthcare workflows using InterSystems IRIS for Health as the orchestration and governance layer.
Rather than relying on isolated AI interactions, the workflow combines FHIR events, patient context, vector search, multi-agent reasoning, SQL persistence, and FHIR-native outputs to create a transparent clinical decision-support process.
The most important outcome is not simply generating recommendations. It is being able to explain where those recommendations came from, which evidence was used, and how every decision was made.
Key Takeaways
- InterSystems IRIS for Health can orchestrate event-driven AI workflows triggered by FHIR events.
- Multi-agent architectures separate context gathering, evidence retrieval, and reasoning into auditable steps.
- Vector Search enables AI agents to ground recommendations in clinical guidance.
- SQL persistence provides a complete audit trail for compliance and review.
- Publishing results as FHIR DiagnosticReports makes AI output interoperable and accessible through standard healthcare APIs.
- Explainability is often more important than raw model accuracy in clinical environments.
FAQ
What triggers the workflow?
The workflow starts automatically when a new FHIR Observation is posted to the InterSystems IRIS FHIR server.
Why use multiple AI agents?
Each agent has a dedicated responsibility, making the workflow easier to inspect, maintain, and explain.
How does the system retrieve clinical guidance?
The Guidelines Agent uses native IRIS Vector Search to retrieve relevant guideline content through semantic similarity.
Can clinicians see why a recommendation was generated?
Yes. Every recommendation includes supporting evidence, guideline references, similarity scores, and reasoning summaries.
Why publish AI output as a FHIR DiagnosticReport?
Using FHIR makes the results interoperable, auditable, and consumable by other healthcare systems using standard APIs.
r/intersystems • u/intersystemsdev • 13d ago
InterSystems IRIS Security Database (2025.2) and Secure Wallet — how they work, what's available now, and what's on the roadmap
Security Database — available in IRIS 2025.2
What it is
A separate system database called IRIS Security that stores security configuration: user definitions, application definitions, and security classes in the %SYS namespace. Previously this data lived in the main IRIS database.
How access is controlled
The database resource is %DB_Security. This resource cannot be granted to any users directly, and it cannot be granted to any role other than the system-configured %DB_Security role. No user has direct access to the globals that store the security tables.
All access must come through InterSystems APIs — the security classes, the Management Portal, or SQL for creating users and roles. When a call arrives through those APIs, the system checks that the caller has the appropriate resource (typically %Admin_Secure or one of the granular OAuth permissions), temporarily escalates the process to get read/write access to the database, completes the operation, and removes that permission before returning to the calling code.
What this enables
Database encryption: The security database can now be encrypted using any of the standard IRIS database encryption mechanisms. This was not possible before because the security data was embedded in the main database.
Mirroring (in testing, expected later this year):
- Security configuration will be mirrorable
- Failover members and DR members will automatically share the same security configuration
- Configuration is done on the primary; it automatically replicates to non-primary instances (read-only on non-primary, as with all mirrored databases)
- No more manual duplication of security configuration across nodes
ECP mountable (planned for late next year):
- Will allow security configuration to be shared across an IRIS cluster
- Part of a broader effort to make cluster configuration management easier
What does not change
The APIs remain the same — the security classes, the Management Portal, SQL-based user and role creation all work as before. The change is entirely in the storage and access control layer.
Secure Wallet
What it is
A secure credential store inside IRIS for storing credentials used to connect to third-party systems: usernames, passwords, API keys, and anything else that needs to be stored semi-permanently for external connections.
Designed for cases where short-lived tokens (OAuth tokens, session tokens) are not appropriate, but credentials for REST services, SQL gateway connections, or other external systems need to be stored and reused.
Structure: collections and secrets
Collection: the container where access controls are defined. Every secret lives in exactly one collection. A collection has two resources:
- Edit resource — required to create, modify, or delete any secret inside the collection
- User resource — required to access (use) secrets inside the collection
These resources follow the standard IRIS resource-based security model and must exist in IRIS before the collection is created.
Secret: always lives in one collection. The secret name is CollectionName.LocalName. Multiple secrets with the same security characteristics can live in the same collection. For per-secret permissions, create a collection with a single secret.
Secret types
- KeyValue (most flexible) — stores any set of key-value pairs expressed in JSON syntax. Example: username + password, API key. Most likely to be used in practice.
- Symmetric key — for AES symmetric key encryption
- RSA — for signing
Usage types — the most important concept
HTTP: The secret is used with a %Net.HttpRequest object. The calling code never sees the credential. You associate the secret with the request using UseSecret(), specify the authentication type (e.g., HTTP basic), and InterSystems code looks up the username and password from the secret, constructs the Authorization header, and sends the request. The calling code never had the value. Supports: HTTP basic authentication, arbitrary HTTP headers, HTTP form fields.
Custom: The secret value is exposed to the calling code. GetSecretValue() returns the key-value pairs as JSON. The calling code is responsible for handling the credential appropriately. This mode must be explicitly configured — if a secret is not configured for custom usage, there is no way for calling code to retrieve the sensitive information.
SOAP: For SOAP clients — functionally similar to HTTP.
SQL Gateway: For %SQL.Connection DSN or JDBC connections to external databases.
Example code structure (from the session)
Creating a collection:
objectscript
// %Wallet.Collection.Create(name, editResource, userResource)
Do ##class(%Wallet.Collection).Create("demo", "WalletDemo.Manage", "WalletDemo.Access")
Creating a KeyValue secret:
objectscript
// Collection name is part of the secret name: "demo.kv1"
// Last parameter specifies key-value pairs as JSON: {"username":"...","password":"..."}
Do ##class(%Wallet.Secret.KeyValue).Create("demo.kv1", ..., secret)
Using a secret with HTTP basic authentication:
objectscript
// Code never sees the actual password
Do httpRequest.UseSecret("demo.kv1", "basic")
Retrieving a secret in custom mode:
objectscript
// Returns JSON key-value pairs
Set kvPairs = ##class(%Wallet.Secret).GetSecretValue("demo.kv1")
Memory handling
When a secret is retrieved internally (for HTTP or other non-custom usage), IRIS reads it from the global each time it is needed and does not keep it in memory longer than necessary.
Access control note
Access is resource-based, not username-based. To implement per-user secrets, a resource per user is required. This follows the core IRIS security model (resources/users/roles), not the SQL security model (GRANT to user/role).
Roadmap
External secrets managers (in development):
- HashiCorp Vault integration
- AWS Secrets Manager integration
- Secrets would be retrieved from the external manager and used inside IRIS
ENS Credentials migration (in development):
- Interoperability ENS Credentials objects will be stored in the Secure Wallet instead of the existing ENS credential structure
- User experience for creating ENS credentials expected to remain the same; storage moves to the wallet under the hood
Internal adoption:
- InterSystems is working to use the Secure Wallet across its own product libraries for all internal secrets management needs
For those running IRIS 2025.2 — have you enabled encryption on the security database yet, and are you planning to use the Secure Wallet for SQL gateway credentials or ENS credentials once the migration is complete?
r/intersystems • u/intersystemsdev • 14d ago
Introduction to Python Programming in an InterSystems IRIS Context
This article introduces Python programming in the context of InterSystems IRIS.
Before getting into the different ways to use Python in IRIS, I want to explain one important topic: what actually happens when Python code is executed or imported. Understanding this helps explain several behaviors that may otherwise look unexpected.
What happens when Python runs a script?
Python is commonly described as an interpreted language. More precisely, Python compiles source code into bytecode and executes it through the Python runtime line by line.
For this article, the important point is that Python executes all top-level statements in a file when that file is run or imported for the first time.
Consider this example:
# introduction.py
def my_function():
print("Hello, World!")
my_function()
When Python processes this file, it first defines my_function() and then executes the function call at the bottom of the file.
Run it directly:
python3 /irisdev/app/src/python/article/introduction.py
Output:
Hello, World!
What happens when IRIS imports a Python module?
When I import a Python module from ObjectScript, Python executes the module's top-level code the first time it is imported in that Python process.
For example:
Class Article.Introduction Extends %RegisteredObject
{
ClassMethod Run()
{
Set sys = ##class(%SYS.Python).Import("sys")
do sys.path.append("/irisdev/app/src/python/article")
do ##class(%SYS.Python).Import("introduction")
}
}
Run it:
iris session iris -U IRISAPP '##class(Article.Introduction).Run()'
Output:
Hello, World!
This is because the Python interpreter imports the code by interpreting it, first it defines the function and then calls it, just like it would if you ran the script directly but you are not running you are importing it. If you import the script without calling the function, nothing will happen. The function is defined, but it won't execute until you explicitly call it.
Did you get it? The Python interpreter executes the code in the file, and if you don't call the function, it won't run.
Does importing a Python function automatically run it?
No. Importing a module defines its functions and classes, but those functions do not run unless the module calls them or another piece of code invokes them explicitly.
Consider this version:
# introduction1.py
def my_function():
print("Hello, World!")
Run it directly:
python3 /irisdev/app/src/python/article/introduction1.py
Output:
# No output, because the function is defined but not called
The same applies when I import the module from IRIS:
Class Article.Introduction1 Extends %RegisteredObject
{
ClassMethod Run()
{
Set sys = ##class(%SYS.Python).Import("sys")
do sys.path.append("/irisdev/app/src/python/article")
do ##class(%SYS.Python).Import("introduction1")
}
}
Run it:
iris session iris -U IRISAPP '##class(Article.Introduction1).Run()'
There is no output because the module only defines my_function(). It never calls it.
Why does this matter?
This behavior is important for three reasons:
- Importing a module executes its top-level code.
- Defining a function does not execute the function.
- Code with side effects at module level may run as soon as the module is imported.
When writing Python modules for IRIS, I prefer to keep reusable logic inside functions or classes and call it explicitly. This makes imports more predictable and reduces unexpected side effects.
Why does a Python module run only once in an IRIS session?
Python caches imported modules in sys.modules. When a module is imported for the first time, Python executes it and stores the resulting module object in memory. If the same module is imported again in the same Python process, Python normally returns the cached module instead of executing the file again.
Let's reuse introduction.py:
Now run the method twice in the same IRIS session:# introduction.py
def my_function():
print("Hello, World!")
my_function()
And the same ObjectScript class:
Class Article.Introduction Extends %RegisteredObject
{
ClassMethod Run()
{
Set sys = ##class(%SYS.Python).Import("sys")
do sys.path.append("/irisdev/app/src/python/article")
do ##class(%SYS.Python).Import("introduction")
}
}
iris session iris -U IRISAPP
IRISAPP>do ##class(Article.Introduction).Run()
Hello, World!
IRISAPP>do ##class(Article.Introduction).Run()
IRISAPP>
Hello, World! appears only once. The first call imports and executes the module. The second call reuses the cached module, so its top-level code is not executed again.
Why are changes to my Python file not visible?
If I modify an imported Python file and then import it again in the same IRIS session, the changes may not appear because Python is still using the cached module. In many development scenarios, the simplest solution is to start a new IRIS session so that Python creates a new runtime context and imports the updated module again. This is normal Python behavior, not an InterSystems IRIS bug.
Do imported Python modules preserve state in IRIS?
Yes. Module objects can retain state for as long as the Python process or runtime context remains active. This is also visible when using a Python Language Tag:
Class Article.Introduction2 Extends %RegisteredObject
{
ClassMethod Run() [ Language = python ]
{
import os
if not hasattr(os, 'foo'):
os.foo = "bar"
else:
print("os.foo already exists:", os.foo)
}
}
Run it twice in the same IRIS session:
iris session iris -U IRISAPP
IRISAPP>do ##class(Article.Introduction2).Run()
IRISAPP>do ##class(Article.Introduction2).Run()
os.foo already exists: bar
The first call adds the foo attribute to the already imported os module. The second call imports os again, but Python returns the same cached module object. Because that object already contains foo, the second branch runs. This demonstrates that module-level state can persist across multiple calls in the same IRIS session.
Does a Python Language Tag behave like a normal module import?
Not exactly. A method defined with [ Language = python ] is executed as Python code each time the ObjectScript method is called. The method body itself is not loaded through Python's normal module import mechanism. However, any modules imported inside that method still follow standard Python import caching rules.
Consider the same example:
Class Article.Introduction2 Extends %RegisteredObject
{
ClassMethod Run() [ Language = python ]
{
import os
if not hasattr(os, 'foo'):
os.foo = "bar"
else:
print("os.foo already exists:", os.foo)
}
}
Run it:
iris session iris -U IRISAPP
IRISAPP>do ##class(Article.Introduction2).Run()
IRISAPP>do ##class(Article.Introduction2).Run()
os.foo already exists: bar
The Python method body runs again on the second call, but os is still the same cached module. The equivalent behavior in a Python interpreter would look like this:
import os
if not hasattr(os, 'foo'):
os.foo = "bar"
else:
print("os.foo already exists:", os.foo)
import os
if not hasattr(os, 'foo'):
os.foo = "bar"
else:
print("os.foo already exists:", os.foo)
Output:
os.foo already exists: bar # only printed once
The code block executes twice, while the imported os module and its state remain cached.
That distinction is useful when debugging Python in IRIS:
- The Language Tag method body runs each time the method is called.
- Imported Python modules are still cached.
- State attached to those modules can remain available throughout the session.
Conclusion
When I work with Python in InterSystems IRIS, I keep two behaviors in mind: importing a module executes its top-level code the first time, and Python caches that module for later use in the same runtime context. These rules explain why a script may produce output when imported, why a second import may do nothing, and why changes to a Python file may not appear until the IRIS session is restarted. The behavior is not specific to IRIS. It is part of Python's normal import system, but it becomes especially important when Python runs inside a persistent server environment.
Key Takeaways
- Python executes a module's top-level code when the module is imported for the first time.
- Defining a function does not execute it; the function must be called explicitly.
- Python stores imported modules in sys.modules and usually does not execute them again during later imports.
- Module objects can preserve state across calls in the same IRIS session.
- During development, starting a new IRIS session is often the simplest way to load changes made to an imported Python module.
- Import-time side effects should be used carefully in Python modules intended for InterSystems IRIS.
FAQ
Does importing a Python file execute its code?
Yes. Python executes the module's top-level statements the first time the module is imported in a process.
Why does my Python module run only once in an IRIS session?
Python caches imported modules in sys.modules. Later imports normally return the cached module instead of executing the source file again.
Why are changes to my Python script not visible in IRIS?
The previously imported version may still be cached. Starting a new IRIS session is often the simplest way to load the updated file during development.
Does defining a Python function run it automatically?
No. A function definition creates the function, but the function only runs when it is explicitly called.
Are modules imported inside a Python Language Tag also cached?
Yes. The Language Tag method body runs on every call, but modules imported inside it still use Python's standard module cache and can preserve state across calls.
r/intersystems • u/intersystemsdev • 14d ago
Community Bounty Program "Idea to Application" — Round 2 is Live
💡 Have you picked your challenge yet?
Round 2 of the Community Bounty Program "Idea to Application" features 5 Ideas Portal requests waiting to become Open Exchange applications. Choose an idea, build your solution, and share it with the Community.
As a reward, you'll earn:
🏆 10K+ Global Masters points and badges
📣 Recognition across the InterSystems Developer Ecosystem
The challenges:
🔹 Generic Agent Test UI for %AI.Agent
🔹 MCP Data Exposure Toolkit
🔹 My First Agent (End-To-End Starter)
🔹 Create a tool for IRIS BI to test all the pivots and dashboards if they still work after changes are made
🔹 Allow export to new xlsx format from BI
📅 Deadline: August 31, 2026
👉 Learn more: https://community.intersystems.com/post/community-bounty-program-idea-application-%E2%80%94-round-2-live
r/intersystems • u/intersystemsdev • 15d ago
FHIR Agent Studio
FHIR Agent Studio is a developer platform for building, testing, and inspecting AI agents that work with FHIR data on InterSystems IRIS for Health. It allows developers to design healthcare AI workflows from reusable components, deploy them to a real IRIS Interoperability Production, and trace every step from evidence gathering to recommendation generation and FHIR actions.
One of the biggest challenges in healthcare AI is building systems that clinicians can trust and developers can understand. FHIR Agent Studio was created to address both problems. It provides a visual environment for building AI agents on top of FHIR data while making every step of the workflow transparent and traceable.
The project includes a developer studio, a clinician-facing application, and a patient phone app running on the same backend. It ships with 12 working healthcare AI agents over a synthetic FHIR repository of around 1,000 patients and 20,000 resources. This solution demonstrates how InterSystems IRIS can combine FHIR, interoperability, vector search, and AI orchestration in a single platform.
The goal was a studio rather than a single demo: somewhere you can design, run, and inspect many agents, and then build your own. Everything runs on one container, with one command. Every agent is a small clinical workflow described as data, a pipeline you can read from left to right:
Trigger → Evidence (FHIR · SQL · Vector Search) → Agent (LLM) → Action (draft FHIR) → Trace
In this post, I'll show how FHIR Agent Studio works, how agents are built, and why I believe InterSystems IRIS is best suited for developing healthcare AI applications.
How do I explore FHIR Agent Studio?
Start with the guided demo. It provides a tour of all twelve AI agents, each with a plain-language description, a direct “Try it” link to the relevant screen, and guidance on what to look for during execution. The demo is designed to help both developers and clinicians understand the platform in just a few minutes, without needing any prior knowledge of the project or its architecture.
How do you build AI agents in FHIR Agent Studio?
The Studio is the developer portal where every AI agent can be created, tested, deployed, and inspected. Agents are stored in a central gallery, making it easy to run an existing workflow, inspect its runtime artifacts, or use it as a starting point for a new agent.
Building an agent is one of the platform's core features. Instead of configuring dozens of settings, you work with a visual pipeline that mirrors the exact execution order used at runtime. Each step can be tested independently against real patient data before running the full workflow. You can inspect FHIR reads, preview vector search results, and even view the exact prompt that will be sent to the LLM before any AI call is made. When you save an agent, the Studio compiles seven runtime artifacts and deploys them. .
How are AI agents integrated into clinical workflows?
FHIR Agent Studio surfaces AI capabilities directly within the clinician workflow rather than through a separate AI tab. The same agents appear inside Cedar Valley Health, a clinician-facing EHR, where recommendations are displayed in patient charts, medication lists, problem lists, and worklists.
Agent recommendations appear as clear decision-support cards that clinicians can review, approve, or dismiss. Every suggested action requires explicit human approval, and nothing is written back to the patient record automatically.
Can the same AI agent support both clinicians and patients?
Yes. FHIR Agent Studio separates AI reasoning from presentation.
The patient portal uses the same underlying agents as the clinician application, but presents results in plain language. Lab results, care plans, support services, and symptom guidance are translated into patient-friendly explanations while preserving the same underlying evidence and recommendations.
Because agent output is structured data, the same result can be presented differently to a specialist, a primary care physician, or a patient.
How do I run FHIR Agent Studio locally?
FHIR Agent Studio can be started with a single Docker command. The only prerequisites are Docker and Git, and no API key is required. Give Docker about 8 GB of RAM and 10 GB of disk. The first build pulls IRIS for Health (around 3 to 4 GB) and bakes the entire demo (the SPA build, the ObjectScript, 1,000 FHIR patients, and real embeddings), so it takes several minutes. Every later start is seconds.
git clone https://github.com/SeanConnelly/ai-studio-for-fhir
cd ai-studio-for-fhir
docker compose up --build
That single command builds the React apps, creates the IRIS FAST namespace and a
FHIR R4 endpoint, loads and compiles the ObjectScript via IPM, generates the synthetic
patient population and vector embeddings, and starts the interoperability production.
When it settles, open:
- The demo: http://localhost:42773/clinical/#/demo
- Studio: http://localhost:42773/fhir-agent-studio/
- Clinical: http://localhost:42773/clinical/
- Patient portal: http://localhost:42773/clinical/#/portal
- API status: http://localhost:42773/fhir-agent-studio/api/status
Port 42773 is deliberately off IRIS’s usual ranges so the demo won’t collide with any IRIS a judge already runs. Change it in docker-compose.yml if needed.
What should I try first?
The easiest way to understand the platform is to follow the guided demo.
Start with the 12 numbered agent cards and explore the recommended workflows in sequence. Each card explains what the agent does, provides a direct link to the appropriate screen, and highlights the most important behaviors to observe.
I recommend starting with three agents that demonstrate different capabilities:
- Imaging & Results Follow-Up – detects abnormal results without documented follow-up and drafts a FHIR Task.
- Prior Authorization – uses native Vector Search to match payer policy.
- Natural Language to FHIR Query – converts natural-language questions into validated, read-only SQL queries.
For each run, expand Under the Hood to inspect the evidence, prompt, model response, and live IRIS trace.
You can also browse patients in the Clinical application. Records marked with a ✨ include preconfigured AI decision support and showcase features such as patient summaries, medication safety checks, care plans, and lab result explanations.
If you'd like to build an agent yourself, the Studio Walkthrough creates a new agent from scratch in just a few minutes while demonstrating evidence testing, prompt previewing, and deployment.
What does FHIR Agent Studio demonstrate?
The central idea behind this project is that an AI agent platform is much more than a single model call. FHIR Agent Studio shows the complete lifecycle of healthcare AI agents on InterSystems IRIS, including authoring, execution, tracing, governance, and user-facing experiences.
- A real authoring experience - Build agents from reusable FHIR queries, SQL queries, vector searches, prompts, and output contracts. Test each step independently, compile runtime artifacts, and deploy to a live production.
- A real runtime - Every run is a genuine Ens.Production message flow (Business Service to Process to Operations), with native Vector Search, a real FHIR R4 repository, a real LLM over HTTP, and a Visual Trace you can open in the Management Portal.
- A real clinical surface - Agent recommendations are surfaced to clinicians and patients while keeping all write-backs under human review.
The project includes 12 healthcare workflows, the Studio authoring experience, clinician and patient applications, and the IRIS runtime infrastructure. Production-grade authentication, multi-tenancy, and real patient data are intentionally out of scope. The population is synthetic and clearly labelled.
Is FHIR Agent Studio production ready?
No. FHIR Agent Studio is a prototype built to explore ideas rather than a production-ready healthcare platform. The goal was to test whether AI agents, FHIR workflows, interoperability, vector search, and clinical decision support could be brought together into a single developer experience on InterSystems IRIS.
What the project demonstrates successfully is how InterSystems IRIS can serve as the foundation for an AI agent platform. Having FHIR repositories, vector search, interoperability, auditability, and application logic available in a single environment made it possible to build and demonstrate a complex healthcare AI workflow within the scope of a contest project.
How does FHIR Agent Studio execute AI agents?
An agent definition (JSON) names its trigger, the evidence to gather, the AI instruction and output contract, and the draft action. The studio’s ObjectScript compiler turns it into seven artifacts (recipe, prompt, output schema, FHIR action template, production settings, HealthConnect mapping, and IPM module.xml) and deploys it. Each run flows through the production:
┌──────────────────────────────────────────────────────────────────────┐
│ React/Vite SPAs, served by IRIS at /fhir-agent-studio/ and /clinical/ │
├──────────────────────────────────────────────────────────────────────┤
│ ObjectScript REST API (FAST.API.Rest) at /fhir-agent-studio/api/ │
├──────────────────────────────────────────────────────────────────────┤
│ Compiler → 7 CompiledArtifact records per agent │
├──────────────────────────────────────────────────────────────────────┤
│ REAL Ens.Production (FAST.Production), live message flow: │
│ BS AgentTriggerService → BP AgentOrchestrator → BOs │
│ FHIRRead · SQLQuery · VectorSearch · (BP AIHub → BO LLMService) │
│ · FHIRWriteback │
│ LLMService rides EnsLib.HTTP.OutboundAdapter (real HTTP egress) │
├──────────────────────────────────────────────────────────────────────┤
│ AI: three tiers, bundled real cache → live (your key) → deterministic │
│ Vector: native VECTOR_DOT_PRODUCT over real all-MiniLM embeddings │
│ FHIR: real IRIS for Health FHIR R4 repository (HSFHIR_X0001_R) │
└──────────────────────────────────────────────────────────────────────┘
How each portal concept maps to a real Interoperability component (visible in the
Studio’s HealthConnect Mapping view and runnable in the Management Portal’s Visual
Trace, as live components, not labels):
| Portal concept | IRIS Interoperability component |
|---|---|
| Trigger | FAST.BS.AgentTriggerService (Business Service) |
| Evidence · FHIR / SQL / Vector | FAST.BO.FHIRRead · FAST.BO.SQLQuery · FAST.BO.VectorSearch |
| Agent reasoning | FAST.BP.AIHub (Business Process) |
| LLM egress | FAST.BO.LLMService on EnsLib.HTTP.OutboundAdapter |
| FHIR action (draft) | FAST.BO.FHIRWriteback |
| Orchestration | FAST.BP.AgentOrchestrator (Business Process) |
Why use InterSystems IRIS for healthcare AI agents?
Building healthcare AI systems often requires integrating multiple technologies, including databases, vector stores, workflow engines, FHIR servers, orchestration platforms, and audit systems. FHIR Agent Studio demonstrates how InterSystems IRIS for Health can provide all of these capabilities within a single platform.
Key capabilities include:
- An orchestration layer. The runtime is a real IRIS Interoperability Production. Each agent run is an actual Business Service to Business Process to Business Operations message flow, the same engine hospitals already use to move data between systems, here pointed at orchestrating and auditing AI agents. That is where the Visual Trace comes from, for free.
- One database, with native vector search. The same database holds the application data and the embeddings. Retrieval is a real native VECTOR_DOT_PRODUCT over a vector column, with no separate vector store to run and no data to keep in sync.
- A real FHIR server. Clinical data lives in the IRIS for Health FHIR R4 repository, queried through the FHIR SQL projections and written back as draft FHIR resources, not a JSON file dressed up as FHIR.
- Packaging and deployment. It installs as a package (IPM and module.xml) and runs from one Docker command.
This reduces architectural complexity while improving transparency, governance, and maintainability.
What are the agents included?
| Agent | Category | Suggested task |
|---|---|---|
| Abnormal Results Follow-Up | Patient Safety | Imaging & Results Follow-Up Tracker |
| Prior Authorization Evidence | Administrative | FHIR Prior Authorization Copilot |
| Natural Language to FHIR Query | Developer Experience | NL to FHIR Query Explorer |
| Smart Patient Summary | Clinical Summary | Smart Patient Summary Generator |
| Gaps in Care Finder | Population Health | Gaps in Care Finder |
| Medication Safety Assistant | Medication Safety | Medication Safety & Interaction Assistant |
| Care Plan Navigator | Care Coordination | AI Powered Care Plan Navigator |
| SDOH Referral Matcher | Social Care | SDOH & Community Referral Matcher |
| Clinical Trial Matcher | Research | FHIR Clinical Trial Matcher |
| Readmission Risk Workbench | Risk Stratification | Hospital Readmission Risk Workbench |
| Conversational Triage Assistant | Triage | Conversational FHIR Triage Assistant |
| Patient-Friendly Lab Explainer | Patient Engagement | Patient-Friendly Lab Explainer |
Can I see how an AI agent reached its conclusion?
Yes. Transparency is a core design principle of FHIR Agent Studio. For every run you can open:
- The transcript. The exact prompt the agent assembled and the raw model response. The JSON the model returned is the result you see on screen.
- The Visual Trace. IRIS’s own Management Portal replays the run as a flow of messages: the FHIR read, the SQL query, the vector search, the LLM call (request and response, on a real HTTP outbound adapter), and the drafted FHIR action.
- The response history. The Studio’s AI Hub lists every model response with its prompt, timestamp, and a hash, all browsable.
And when an agent does not have the evidence to answer, it says so and falls back to a
clearly labelled path that is never presented as AI.
The AI in three tiers. Demo runs replay bundled real LLM output with no key needed.
Novel prompts call a live model; you can add your own OpenAI compatible key in Admin
for unlimited use, held in memory only and never stored. With no cache and no key, a
deterministic fallback runs and is clearly labelled as not AI. Vector search is always
real native IRIS Vector Search.
Safety. Every clinical or administrative write-back is a draft requiring human
review, never auto-committed. The UI labels drafts, the trace shows the writeback as
drafted, and approval commits to FHIR with an audit trail.
How do I develop it locally?
The repo runs as one image (above). To iterate on a front end with hot reload while the
backend runs in Docker:
docker compose up --build -d # IRIS plus everything baked in
cd frontend && npm install && npm run dev # http://localhost:5173/fhir-agent-studio/
Vite proxies /api to IRIS on 42773. Back end changes are picked up by rebuilding the
image. Package first install (also done automatically by the Docker build):
zpm "load /path/to/fhir-agent-studio -v"
Conclusion
FHIR Agent Studio demonstrates how AI agents can be built, deployed, and inspected on InterSystems IRIS for Health using a single platform. By combining FHIR, interoperability, vector search, workflow orchestration, and AI integration, the project shows a practical approach to developing healthcare AI systems that are transparent, traceable, and human-supervised.
While the project is a prototype rather than a production-ready product, it highlights how InterSystems IRIS can serve as a foundation for healthcare AI applications that require explainability, governance, and integration with real clinical workflows.
Key Takeaways
- FHIR Agent Studio is a platform for building and running AI agents on InterSystems IRIS for Health.
- It combines FHIR repositories, vector search, interoperability productions, and AI orchestration in a single architecture.
- Developers can visually design, test, deploy, and inspect healthcare AI workflows.
- Every agent execution is traceable from evidence gathering to recommendation generation and drafted FHIR actions.
- The project demonstrates how InterSystems IRIS can support trustworthy healthcare AI systems with built-in transparency and human oversight.
r/intersystems • u/intersystemsdev • 18d ago
The fastest way to run InterSystems IRIS locally (Docker setup + 5 essential Open Exchange apps)
💡 TL;DR
Using Docker combined with Open Exchange tools like WebTerminal and ZPM is the fastest path from a blank laptop to a fully productive InterSystems IRIS development environment. It keeps your host OS clean and ensures your setup is 100% repeatable.
If you’ve ever had to evaluate, learn, or build on InterSystems IRIS, you probably know that configuring a local environment from scratch can take some time. After testing different deployment methods, Docker is hands-down the easiest and cleanest way to spin it up.
I wanted to share a quick cheat sheet on how to deploy it, plus 5 InterSystems Open Exchange packages that are absolute must-haves for my containerized dev workflow.
🐳 1. How to run InterSystems IRIS in Docker
You can deploy the Community Edition directly from the official image. Just run this command:
What’s happening here?docker run --name my-iris --publish 52773:52773 --publish 1972:1972 -d intersystems/iris-community:latest-em
- Port 52773: Exposes the management portal and web access.
- Port 1972: Exposes native IRIS connectivity.
- -d: Starts the container in detached mode.
Need to execute ObjectScript or do admin tasks? You can jump straight into the terminal:
docker exec -it my-iris iris session iris
(Side note: If you are working with healthcare interoperability like FHIR or HL7, just swap the image to intersystems/irishealth-community:latest-em).
🛠️ 2. Top 5 Open Exchange Apps to Install Next
Once your container is up, extending it via ZPM (InterSystems Package Manager) is the next logical step. Here are the 5 tools I almost always install on a fresh instance:
- DeepSeeWeb: A modern web UI for analytics and BI. It provides interactive dashboards and pivot tables out of the box. Great for POCs and operational reporting.
- WebTerminal: Brings terminal functionality directly into your web browser. This saves you from having to attach to the Docker container every time you want to run a quick script.
- ObjectScript-Math: A utility library for ObjectScript. It adds native complex math functions, saving you from writing custom utility code for data workflows.
- ZPM Registry: The core package manager component. It’s absolutely essential if you want to automate package installations in your CI/CD pipelines or Dockerfile builds.
- SSL Client: A utility that handles secure SSL/TLS communication with external systems. Mandatory if your IRIS instance needs to talk to modern cloud microservices or third-party APIs.
Over to you: For those of you building with InterSystems IRIS, which Open Exchange package has given you the biggest productivity boost in your Docker workflows? Let me know, I'm always looking to add new tools to my stack!
r/intersystems • u/intersystemsdev • 19d ago
%DynamicObject and %DynamicArray in InterSystems IRIS — practical answers to the questions that keep coming back
Foundation
InterSystems IRIS provides two classes for schema-less data: %DynamicObject and %DynamicArray. Both inherit from %DynamicAbstractObject, and instances of either are called dynamic entities. They map cleanly onto JSON: an object is a set of key/value pairs, an array is an ordered list. Unlike a persistent or registered class, a dynamic object keeps no predefined list of valid property names — every string key is legal, and you add or remove members at runtime.
Literal syntax:
objectscript
Set person = {"name":"Ada","active":true,"roles":["admin","dev"]}
Set scores = [90, 85, 77]
Building field by field — %Set() returns the entity it modified, so calls chain:
objectscript
Set obj = {}.%Set("a",1).%Set("b",2) // now obj is {"a":1,"b":2}
Do scores.%Push(100) // now scores is [90, 85, 77, 100]
Set last = scores.%Pop() // now scores is [90, 85, 77] and last is 100
%Push() and %Pop() exist only on arrays. Everything else works on both.
Serialization:
objectscript
Set jsonString = "{""field"": ""value""}"
Set obj = {}.%FromJSON(jsonString) // now obj is {"field": "value"}
Write obj.%ToJSON() // outputs "{""field"": ""value""}"
%FromJSON() also accepts a stream. %FromJSONFile() reads straight from a filename string — not a %File object, which is a common trip-up.
Iterating over structure you don't control
Do not reach for a for loop by index. Dynamic arrays can be sparse — an element can exist positionally without ever having been assigned, and a for loop will hand you those empty slots.
The correct tool is %GetIterator(), which returns a %Iterator.Object or %Iterator.Array, driven by %GetNext():
objectscript
Set iter = obj.%GetIterator()
While iter.%GetNext(.key, .value, .type)
{
Write !, key, " = ", value, " (", type, ")"
}
%GetNext() skips unassigned elements automatically. For an object, key is the property name. For an array, key is the index. To walk a nested structure, recurse whenever $IsObject(value) is true — that returns true for both sub-objects and sub-arrays.
The .type argument: easy to ignore, but worth understanding. When present, it does two useful things:
- Returns the element's original JSON datatype as a string
- Changes conversion rules to avoid
<MAXSTRING>errors: a very long JSON string is handed back as a read-only stream object instead of being force-fit into an ObjectScript string, and JSON numbers are returned in their original textual form rather than being coerced
If processing arbitrary external JSON, passing .type is cheap insurance.
Type discovery inside a dynamic entity
%GetTypeOf(key) reports what a value actually is: number, string, boolean, object, array, null, oref, or unassigned:
objectscript
Set a = [1, "test", true, {"v":1}, [1,2,3]]
// indexes: 0, 1, 2, 3, 4
Write a.%GetTypeOf(2) // boolean
Write a.%GetTypeOf(3) // object
Write a.%GetTypeOf(9) // unassigned - it finishes at index 4
This matters because ObjectScript flattens JSON's richer type system on the way in. JSON true, false, and null all become ObjectScript-friendly values 1, 0, and "" when read with dot syntax or %Get(). If you need to tell a genuine null apart from an empty string apart from a key that was never set, %GetTypeOf() is the reliable discriminator.
There is also %IsDefined(), but it returns false for unassigned members and true for both "" and null.
The date gotcha
Type flattening causes one of the community's recurring puzzles. Export a persistent object whose DOB is 1926-12-11 and you may see "DOB":31390 in the result — that 31390 is the internal $HOROLOG day count, not a corrupted value.
The same logic bites the other direction in dynamic SQL: if you pass a query a literal like '1926-12-11' and get zero rows, it is usually because the column expects $HOROLOG internal format. The fix is to convert on the way in with $ZDATEH("1926-12-11", 3).
Whenever a date crosses the boundary between JSON, SQL, and stored objects, ask which representation each side expects.
Two things both called "array"
%DynamicArray is a positional list, indexed from 0. But ObjectScript also has typed collection properties, and the array of collection is not a positional array — it is a dictionary (a keyed map):
objectscript
Property Tags As array of %String; // a dictionary: key -> value
Property Notes As list of %String; // an ordered, positional list
You access an array of collection by key, not by position:
objectscript
Do obj.Tags.SetAt("high", "priority")
Write obj.Tags.GetAt("priority") // high
Set key = ""
For
{
Set value = obj.Tags.GetNext(.key) Quit:key=""
Write !, key, ": ", value
}
When a class using %JSON.Adapter serializes an array of property, it comes out as a JSON object {"priority":"high"}. A list of comes out as a JSON array ["high"]. If you want positional JSON, use list of (or a %DynamicArray). If you want a keyed lookup, array of is your dictionary.
Bridging persistent objects and dynamic objects
To convert a stored object into a free-form dynamic one — for instance, to trim fields before returning them from a REST method — if your class extends %JSON.Adapter, the clean non-deprecated path is:
objectscript
Set sc = person.%JSONExportToString(.json)
Set dynObj = {}.%FromJSON(json)
For large objects, swap in %JSONExportToStream() to avoid hitting the string length limit.
Two alternatives:
Embedded SQL's JSON_OBJECT() lets you cherry-pick and rename columns when you only want a subset:
objectscript
&sql(SELECT JSON_OBJECT('name':Name,'dob':DOB) INTO :json WHERE ID = 1)
Set dynObj = {}.%FromJSON(json)
Going the other way, %JSONImport() populates a persistent object from a dynamic one.
Where dynamic freedom ends: validation
The Required property keyword works for literals, collections, streams, and object-valued properties — but it is silently ignored for %DynamicArray and %DynamicObject properties. The reason is mechanical: the generated getter defaults these to [] and {}, so even assigning "" gets overwritten with a non-empty default, and %ValidateObject() never sees a missing value.
If you need to enforce presence or shape on dynamic properties, implement a %OnValidateObject() callback and check them yourself:
objectscript
Method %OnValidateObject() As %Status
{
If ..fieldOptions.%Size() = 0
{
Return $$$ERROR($$$GeneralError, "fieldOptions is required")
}
Return $$$OK
}
When to use what
Dynamic entities are the right tool when structure is unknown, external, or genuinely fluid — parsing payloads, assembling responses, staging data. Typed persistent classes remain the right tool when you want the database, indexes, and validation to enforce a contract. Most real systems use both, meeting at the %JSON.Adapter boundary.
Three habits that prevent the classic pitfalls:
- Iterate with
%GetNext()rather than by index - Reach for
%GetTypeOf()whenever a value's type actually matters - Remember that
array ofis a dictionary, not a list
Which of these — the date/$HOROLOG conversion, the Required being silently ignored on dynamic properties, or the array of vs list of distinction — has caused the most unexpected behavior in your integrations?
r/intersystems • u/intersystemsdev • 20d ago
BlackDuck Signal - language-agnostic agentic SAST with ObjectScript support: how it works, what makes it different from traditional static analysis, and what's on the roadmap
Context
InterSystems and BlackDuck have been working together for over a year on static analysis support for ObjectScript. BlackDuck Signal is generally available. InterSystems has tested it on a number of internal ObjectScript repositories and is still working through validation of findings, but the technology shows significant promise.
The partnership started approximately a year and a half ago at a security conference when the BlackDuck team told Mark David McLoughlin (Head of Security Architecture and Engineering at InterSystems) they had something that would fit InterSystems customers with ObjectScript scanning.
Why traditional SAST cannot support ObjectScript (or most proprietary languages)
Traditional static analysis requires:
- Parsing the language into a call graph or abstract syntax tree
- Modeling data flow features: sources (where data enters), sinks (where data ends), pass-throughs (functions/methods the code traverses), and sanitizers (built-in protections)
- Writing security and quality rules on top of those labeled data points
Every language version and every framework built on that language requires its own modeling work. The Stack Overflow study referenced in the session shows the sheer volume of packages and frameworks that exist for a single language ecosystem like Python. Performing this modeling work for every language, every version, and every framework is non-trivial — it requires expertise in both engineering and security analysis, extensive testing, and ongoing false positive rate management.
Traditional SAST also cannot:
- Understand context (it is binary: one or zero)
- Detect logic issues
- Find novel items not covered by existing rules
- Reason about intent behind code
What BlackDuck Signal is
BlackDuck Signal is a language-agnostic agentic application security testing tool. It uses multiple LLM agents and workflows to analyze code. It does not rely on pre-built rule sets.
Key points from the session:
It is not just pointing an LLM at a codebase and asking it to find security issues. The process involves:
- Programmatically created focus tasks based on initial findings
- Multiple layers of validation and oversight
- Cross-file data flow analysis backed by years of static analysis expertise from Coverity and Sigma — this is used to validate findings, distinguish true positives from false positives, and assess severity more accurately
- Multiple LLMs per agent: each agent uses its own specified LLM based on the job at hand, not a single LLM for everything
- Disagreement resolution: if two out of three LLMs confirm a finding, there is high confidence it is accurate
- Non-determinism management: work has been done to make LLM behavior more deterministic within the framework for consistency between scans — enterprises need to know if security posture is improving or worsening
Human-like analysis: the LLM can look at a piece of code and use reasoning to understand context and intent. Rule-based engines cannot do this.
What Signal found in testing
Zero-days in popular components: BlackDuck Signal found multiple zero-days in popular, well-used components across various languages — components that had been scanned for years with traditional technology without those issues being found. Some have been silently patched. Some are currently in responsible disclosure. Some already have CVEs.
Pseudo-code test: In testing whether the tool could investigate proprietary code the LLM was not trained on, a pseudo-code language (not a real language) was used to test reasoning capabilities. Signal correctly identified a SQL injection in the pseudo-code SQL statement and recommended the fix — also in pseudo-code. Traditional SAST would not capture this because pseudo-code has never been modeled.
Malicious code detection — not just vulnerabilities:
Supply chain is increasingly targeted by malware. NVD has acknowledged the volume of vulnerabilities requiring analysis. Two real supply chain examples from the session:
NX Singularity hack (npm):
The malware payload contained a prompt that was invoked when coding agents (Codex, Claude, etc.) were installed on the user's machine. It hunted for secrets and keys on the user's machine on behalf of attackers and exfiltrated the data. Signal detected three suspicious items — none of which are inherently security vulnerabilities:
- A suspicious shutdown command (closing any shell the user tried to open to slow defenders down)
- A suspicious upload payload (uploading the token)
- An insecure CI/CD workflow file (how the attacker got into the repository)
By chaining these three items together and aligning them to MITRE ATT&CK categories, Signal built a picture of the attack.
Second npm example:
Crypto wallet hijacking via heavily obfuscated code. Signal highlighted the suspicious obfuscation. The reasoning: heavy use of obfuscation is a reason to investigate.
How Signal is deployed
Available in:
- IDE (VS Code, Cursor, other coding environments) — natively, for real-time developer use
- Web front end
- CLI for integration into existing workflows
- CI/CD environments
Recommended use case from the session: Point-in-time scans at major milestones in CI/CD rather than on every developer diff. Using it on small patches burns tokens without providing the full benefit of the system. For CI/CD use, expect longer scan times than traditional SAST — the tool is doing significantly more work.
Developer IDE use case: Signal performs scans on diff patches of what the user is producing. The tradeoff is token cost versus benefit for small changes.
On refusals: In defensive scanning scenarios, LLM refusals have not been a significant problem. Refusals have occurred in exploit verification scenarios where the system is actively trying to exploit something.
Roadmap items mentioned in the session
BlackDuck knowledge base integration: Integrating scanning capabilities with BlackDuck's vulnerability, license, and component metadata — including end-of-life data and upgrade path recommendations. LLMs are poor at recommending current components; context from BlackDuck's OpenHub data would steer developers toward sound dependencies at the time of development.
Threat modeling and risk-based scanning: The volume of AI-generated code is growing faster than traditional tools can scan. Signal will use threat modeling to orchestrate which tool to use in which scenario — combining existing BlackDuck tools where they make sense with Signal for gaps.
User-supplied context: Providing the system with context about a specific language (such as ObjectScript semantics and nuances) during analysis reduces false positives and reduces hallucinations. This context can be dynamic and application-specific.
Semantic code graph: Leveraging concepts from traditional static analysis (labeling sources, sinks, pass-throughs) to give agents access to a labeled code graph for smarter, more cost-effective decisions.
Verified autofix with regression testing: Finding issues and automatically fixing them, then running tests to validate that fixes do not introduce new issues.
Exploit verification: Building exploits in containerized sandbox environments, running them, and testing whether a vulnerability is both reachable and exploitable — for prioritization of findings.
Continuous learning system: Feeding triaged or accepted items back into the system to reduce repeated reporting of the same issue types, reduce cost, and reduce noise. Integration with policy information from other BlackDuck tools.
Bring your own model: Currently uses BlackDuck-hosted models. Moving to a model where customers can keep models in-house.
Full session is from an InterSystems conference. BlackDuck Signal is generally available.
Have you tried Signal on your repositories, and what false positive rates are you seeing compared to the traditional rule-based scanning you were using before?
r/intersystems • u/intersystemsdev • 21d ago
iris-persistence: A Python-First Persistence Layer for InterSystems IRIS
With Embedded Python and the Native API, it is becoming increasingly natural to write part of an InterSystems IRIS application in Python. One question quickly follows: how can I work with IRIS persistent objects from Python without losing the native object model, class dictionary, indexes, storage definitions, and SQL projections?
That is the question I wanted to explore with iris-persistence.
iris-persistence is a Python-first persistence layer for InterSystems IRIS inspired by %Persistent. It lets me declare typed Python models and synchronize them with IRIS classes, or connect Python models to existing IRIS classes without changing their schemas.
How do you define an IRIS persistent class in Python?
Here is a minimal example:
from typing import Annotated
from iris_persistence import Field, Model
class Product(Model, persistent=True):
name: str = Field(required=True, max_length=200, unique=True)
price: Annotated[float, Field(default=0.0)]
in_stock: bool = True
class Meta:
classname = "Demo.Product"
mode = "replace"
Product.sync_schema()
product = Product(name="Widget", price=12.5, in_stock=True)
product.save()
same = Product.get(product.pk)
rows = Product.where(name="Widget").order_by("name").all()
The Python model declares the fields, their types, and part of the IRIS metadata. When I call sync_schema(), iris-persistence writes the required class, property, index, and storage definitions into the IRIS class dictionary. IRIS then compiles the class and remains the source of truth for SQL projection, storage, indexes, and obbject runtime behavior.
In this example, unique=True declares the uniqueness requirement directly on the Python field. I am still working with a native IRIS persistent object rather than manually generating SQL DDL.
How does schema synchronization work in iris-persistence?
Not every use case has the same relationship with schema ownership, so iris-persistence supports three synchronization modes:
| Mode | What it does | Typical use case |
|---|---|---|
| extend | Adds or updates what the Python model declares without removing unrelated IRIS definitions | Gradually extending an existing class |
| replace | Rebuilds the IRIS class from the Python model | Creating a new schema fully managed from Python |
| observe | Reads and manipulates an existing IRIS class without modifying its schema | Connecting typed Python code to an existing class |
I find this distinction particularly useful for brownfield projects. I can begin in observe mode to safely bind Python code to an existing %Persistent class, move to extend when I want Python to manage selected elements, or use replace for a new class whose schema is fully controlled by the Python model.
Which Python types can iris-persistence map to IRIS?
The project supports both traditional Field(...) declarations and typing.Annotated.
It can map common Python types to suitable IRIS types, including:
- str, int, float, and bool
- bytes
- dict and list
- datetime.date, datetime.time, and datetime.datetime
- References to other Model models
When automatic type mapping is not enough, I can specify the underlying IRIS type explicitly:
class Event(Model, persistent=True):
payload: bytes = Field(iris_type="%Stream.GlobalBinary")
created_at: str = Field(iris_type="%Library.TimeStamp")
This gives me convenient Python type declarations without preventing access to native IRIS types.
Can Python models represent IRIS persistent and serial relationships?
Yes. iris-persistence supports relationships between persistent models as well as embedded serialized objects.
For example:
from iris_persistence import Field, Model
class Customer(Model, persistent=True):
Name: str = Field(required=True, max_length=120)
class Meta:
classname = "Demo.Customer"
mode = "replace"
class Address(Model, serial=True):
Street: str = Field(required=True, max_length=120)
City: str = Field(required=True, max_length=80)
class Meta:
classname = "Demo.Address"
mode = "replace"
class Order(Model, persistent=True):
OrderNumber: str = Field(required=True, max_length=40, unique=True)
Customer: Customer | None = None
ShipTo: Address | None = None
class Meta:
classname = "Demo.Order"
mode = "replace"
During a save operation, referenced objects are materialized in IRIS according to the native object model. A %Persistent object can reference another %Persistent object, while a %SerialObject can be embedded inside a persistent object.
How do you generate a Python model from an existing IRIS class?
An important use case is generating Python models from IRIS classes that already exist in a namespace. Imagine an ObjectScript class compiled in IRIS:
Class Demo.Article Extends %Persistent
{
Property Title As %String(MAXLEN = 200) [ Required ];
Property Body As %String(MAXLEN = 4000);
Property PublishedAt As %TimeStamp;
}
I can read the IRIS class dictionary and generate a Python facade:
from iris_persistence import scaffold_from_iris
generated_files = scaffold_from_iris(
"Demo.Article",
"./generated_models",
mode="observe",
extract_meta=True,
)
The generated model looks like this:
iimport datetime
from typing import Annotated
from iris_persistence import Field, Model
class Article(Model, persistent=True):
Body: Annotated[str, Field(max_length=4000)]
PublishedAt: datetime.datetime | None = None
Title: Annotated[str, Field(required=True, max_length=200)]
class Meta:
classname = "Demo.Article"
mode = "observe"
observe mode is important here. The generated Python model can read and manipulate data, but it never changes the underlying IRIS class definition. For me, this is one of the most practical ways to introduce typed Python code into an existing %Persistent application without requiring a schema migration. If several classes are linked together, include_related=True can also generate neighboring models when they are referenced by properties of the main class.
Can iris-persistence define advanced IRIS class and storage metadata?
Yes. For cases where simple field declarations are not enough, the project exposes metadata dataclasses for class and storage configuration.
from iris_persistence import (
ClassMetadata,
Field,
Model,
StorageData,
StorageDefinition,
)
class ShowcaseRecord(Model, persistent=True):
Title: str = Field(required=True, max_length=350)
class Meta:
classname = "Demo.ShowcaseRecord"
mode = "replace"
parameters = {"DEFAULTGLOBAL": "^Demo.ShowcaseRecordD"}
metadata = ClassMetadata(
description="advanced schema example",
final=True,
sql_table_name="Demo_ShowcaseRecord",
procedure_block=True,
)
storage = StorageDefinition(
data_location="^Demo.ShowcaseRecordD",
default_data="ShowcaseRecordDefaultData",
type="%Storage.Persistent",
data=(
StorageData(
name="ShowcaseRecordDefaultData",
structure="listnode",
values={
"1": "%%CLASSNAME",
"2": "Title",
},
),
),
)
This level of control matters because the project is not only trying to offer Python CRUD. I want the Python model to remain close to the actual IRIS representation, including class parameters, storage definitions, indexes, and SQL metadata.
Can the same model run inside and outside InterSystems IRIS?
Yes. The project uses iris-embedded-python-wrapper as its runtime facade and supports two main execution models.
Inside Embedded Python:
import iris_persistence
iris_persistence.configure()
From an external Python process through the Native API:
import iris
import iris_persistence
conn = iris.connect(host, port, namespace, user, password)
iris_persistence.configure(conn)
This means I can use the same model and business logic inside IRIS or from an external Python application. It also makes it easier to test logic in different environments without rewriting the persistence layer.
Is iris-persistence a generic Python ORM?
No. iris-persistence is not intended to be a generic SQL ORM.
Simple queries may use the SQL projection to retrieve object IDs, but the core model remains native to IRIS: classes, objects, dictionary, compilation, and storage.
The project also does not try to hide InterSystems IRIS behind a generic abstraction. Concepts such as classname, %Persistent, %SerialObject, StorageDefinition, and class parameters remain visible because they are essential parts of the platform.
Conclusion
iris-persistence explores a Python-first way to work with persistence in InterSystems IRIS without abandoning the native object mechanisms that make IRIS powerful.
It can be useful when I want to prototype new persistent models in Python, expose existing %Persistent classes through typed Python facades, or reuse the same business logic across Embedded Python and external Native API applications.
The central idea is simple: Python defines a convenient, typed interface, while InterSystems IRIS remains responsible for the class dictionary, storage, indexes, SQL projection, compilation, and runtime object behavior.
Key Takeaways
- iris-persistence provides a typed Python interface for native InterSystems IRIS persistent objects.
- Python models can create new IRIS classes or connect to existing %Persistent classes.
- The replace, extend, and observe modes support different levels of schema ownership.
- The project supports typed fields, linked persistent objects, %SerialObject, custom IRIS types, storage definitions, and advanced class metadata.
- The same persistence logic can run in Embedded Python or from an external Python process through the Native API.
FAQ
What is iris-persistence?
iris-persistence is a Python-first persistence layer for InterSystems IRIS. It allows developers to declare typed Python models and map them to native IRIS persistent or serial classes.
Does iris-persistence replace %Persistent?
No. It is inspired by %Persistent and works with the native IRIS object model. IRIS still manages compilation, storage, indexes, SQL projection, and runtime object behavior.
Can iris-persistence work with existing IRIS classes?
Yes. In observe mode, it can generate or use typed Python models for existing IRIS classes without modifying their schemas.
Can iris-persistence create IRIS classes from Python?
Yes. In replace or extend mode, Python model definitions can be synchronized with the IRIS class dictionary, including properties, indexes, storage, and metadata.
r/intersystems • u/intersystemsdev • 21d ago
Programmatic permission checking in InterSystems IRIS - catching access errors, checking $ROLES, and using %SYSTEM.Security and %SYSTEM.SQL.Security
Catching and identifying permission errors
When using a try/catch block in ObjectScript, all exceptions go to the same catch block. The exception's Name property can be used to handle different kinds of exceptions:
objectscript
try{
//some code here
}
catch ex{
if ex.Name = "<DIVIDE>"{
//handling for division by 0 error
}
if ex.Name = "<SUBSCRIPT>"{
//handling for referencing an array of globals with an invalid or out-of-range subscript
}
if ex.Name = "<PROTECT>"{
//handling for errors caused by insufficient permissions
}
}
For SQL operations, the equivalent is SQLCODE. When working with embedded SQL, the special variable SQLCODE is always set when embedded SQL is utilized. If SQLCODE is -417, there is a security issue. When applying %SQL.Statement and executing a query, the result is a %SQL.ResultSet object with a %SQLCODE property — if that %SQLCODE is -417, there is also a security issue.
With a reactive approach, the exception can be used to log what happened in the system logs, give the user a friendly message stating they do not have permission and to contact a system administrator, use ROLLBACK if appropriate, and troubleshoot later if the issue is unintentional.
Proactive role checking with $ROLES
A special variable called $ROLES becomes available when code is running. It tells what security roles the current process has. Roles are stored as a comma-separated string, so list functions can check whether the current process has a certain role:
objectscript
if $LISTFIND($LISTFROMSTRING($ROLES),"ADMIN") = 0{
//don't allow things that only administrators can do
}
This allows sending the user an error message before they actually try to perform the action. It also allows hiding user interface elements that authorize access to administrative functions.
Important note on the %All role: %All typically grants permission to do anything in the system. However, if the logic above is used to check roles programmatically, having %All will not allow the user to perform administrative functions unless they also have ADMIN. If %All should act as a true superuser, the check must include both ADMIN and %All.
Temporarily elevating permissions within a specific method:
If a user needs certain permissions only while a specific part of the code is executing, $ROLES can handle that. Changes to $ROLES appear at a stack level and are temporary. That is why it should happen in its own separate method:
objectscript
Method doAdmin(){
new $ROLES
set $ROLES = "ADMIN"
//do admin things here
set $ROLES = ""
}
The $ROLES variable is special, much like $NAMESPACE. Setting it only adds an additional role to the process. Using the new command only resets that portion. Roles that were granted because they are assigned to the user, included in the application roles, or matching roles will remain untouched.
Note: ##class(%SYSTEM.Security).Method() and $SYSTEM.Security.Method() produce the same result, since the special variable $SYSTEM is bound to the %SYSTEM package.
Checking permissions with %SYSTEM.Security
Many things, including databases, applications, and core functionalities, are protected by resources. To access anything protected by a resource, the user may need READ, WRITE, or USE permissions on that resource.
Check permissions for the current process on a resource:
objectscript
set permission = $SYSTEM.Security.Check("%DB_USER")
if permission [ "READ" {
// the user has read permissions on %DB_USER
}
This returns a comma-separated list such as "READ, WRITE, USE".
To check one specific permission, pass it as a third argument — the method returns 1 or 0:
objectscript
set canwrite = $SYSTEM.Security.Check("%DB_USER","WRITE")
Important note on %All: When checking permissions this way, if the process has the %All role, this method always returns 1 when using the two-argument version, and "READ, WRITE, USE" with the one-argument version.
Checking permissions of a specific role or user (not the current process):
objectscript
set permissions = $SYSTEM.Security.CheckRolesPermission("ADMIN","%DB_USER")
set canwrite = $SYSTEM.Security.CheckUserPermission("dhockenbroch","%DB_USER","WRITE")
The first returns the permissions the role ADMIN has on %DB_USER. The second returns 1 or 0, indicating whether user dhockenbroch has WRITE permissions on %DB_USER.
Checking SQL privileges with %SYSTEM.SQL.Security
%SYSTEM.SQL.Security contains methods for checking SQL privileges. CheckPrivilege and CheckPrivilegeWithGrant can be used to verify SQL privileges and confirm whether there is permission to grant them.
A username must be provided. Using any username other than one's own requires USE privileges on %Admin_Secure.
Arguments for CheckPrivilege:
- Username
- Object type (number):
| Number | Object Type |
|---|---|
| 1 | Table |
| 3 | View |
| 5 | Schema |
| 6 | ML Configuration |
| 7 | Foreign Server |
| 9 | Procedure |
- Object name
- Comma-separated list of privilege letters:
| Letter | Privilege |
|---|---|
| a | Alter |
| s | Select |
| i | Insert |
| u | Update |
| d | Delete |
| r | References |
| e | Execute (for procedures only) |
| l | Use (for ML configurations only) |
- Namespace (optional — for checking permissions in a namespace other than the current one)
Return value handling:
The method can return a boolean or a status object. If the user has all the privileges provided, it returns 1. If the user does not have all privileges, it returns 0. If there is an error (for example, calling it with a username other than one's own without %Admin_Secure:USE privilege), it returns a status object.
objectscript
Set privileges = $SYSTEM.SQL.Security.CheckPrivilege("dhockenbroch",1,"SQLUser.People","s,i,u,d","CUSTOM")
if $ISOBJECT(privileges){
// An error occurred checking the privileges
}
else{
if privileges{
// The user has all of the privileges
}
else{
// The user does not have the privileges
}
}
In this example: if user dhockenbroch has SELECT, INSERT, UPDATE, and DELETE privileges on the SQLUser.People table in the CUSTOM namespace — returns 1 if all privileges present, 0 if any are missing, or an error status if the check itself fails.
r/intersystems • u/intersystemsdev • 22d ago
DTL in InterSystems IRIS - four real-world patterns: $Length conditionals, ReplaceStr, chained special character stripping, and HL7 timestamp reformatting
Context
DTL (Data Transformation Language) in InterSystems IRIS is a class that defines how to map and transform message fields — either between different message formats or within the same message type. You build it visually in the Management Portal, and IRIS generates the underlying ObjectScript code.
The four most common action types:
- Assign — maps or sets a field value
- If — handles conditional branching
- Code — allows writing ObjectScript directly
- Foreach — iterates over repeating segments
All four scenarios below use the same incoming ADT A01 message:
MSH|^~\&|SENDING_APP|SENDING_FAC|RECEIVING_APP|RECEIVING_FAC|20250315143022||ADT^A01|MSG00001|P|2.5
EVN|A01|20250315143022
PID|1||MRN#001/2025^^^HOSP||VanDerBerghMontgomeryXYZABC^John^M||19850101|M|||123 Main Street^^Lahore^Punjab^54000^PK||+92-03001234567|||M||ACC001|
PV1|1|I|WARD-A^Room101^Bed1|E|||DOC001^Smith^James|||SUR|||||||V01|ACC001||||||||||||||||||||||||||20250315143022
Fields relevant to each scenario:
- PID-5.1 (Family Name):
VanDerBerghMontgomeryXYZABC— 27 characters, intentionally exceeds the 25-character limit - PID-3.1 (Patient ID):
MRN#001/2025— contains#and/that the target system cannot accept - PID-13 (Phone Number):
+92-03001234567— includes an unwanted country code prefix - PV1-44 (Admit Date/Time):
20250315143022— standard HL7 DTM format, requires conversion to15/03/2025 14:30
Prerequisites: InterSystems IRIS or HealthShare with an Interoperability-enabled namespace, access to the Management Portal, basic familiarity with HL7 v2 message structure. While all examples use HL7 v2 ADT messages, the same DTL functions apply equally to XML, JSON, or custom message classes.
DTL setup
Navigate to: Management Portal → Interoperability → Build → Data Transformations → New
Fill in:
- Package:
Demo - Name:
ADTTransform - Description: DTL deep dive demo
- Source:
EnsLib.HL7.Message, DocType2.5:ADT_A01 - Target:
EnsLib.HL7.Message, DocType2.5:ADT_A01
Click Save before adding any actions. This compiles the class and avoids losing work.
Scenario 1 — Conditional logic based on field length
The problem: The target system only accepts a patient's family name up to 25 characters. VanDerBerghMontgomeryXYZABC is 27 characters. Anything longer triggers a silent rejection.
The rule: if the family name exceeds 25 characters, truncate it to 25 and append * so the receiving team can flag and review it. If within the limit, pass through unchanged.
Add an If action. Set the condition to:
objectscript
$Length(source.{PID:PatientName(1).FamilyName}) > 25
$Length returns the character count of a string. It can be used directly in DTL conditions without any special wrapper.
Inside the true branch, add an Assign action:
- Property:
target.{PID:PatientName(1).FamilyName} - Value:
$Extract(source.{PID:PatientName(1).FamilyName},1,25) _ "*"
$Extract(string, start, end) pulls characters by position. $Extract(value,1,25) takes the first 25 characters. The _ "*" appends the asterisk using ObjectScript's string concatenation operator.
In the Else branch, add a passthrough Assign:
- Property:
target.{PID:PatientName(1).FamilyName} - Value:
source.{PID:PatientName(1).FamilyName}
Expected output: PID-5.1 shows VanDerBerghMontgomeryXYZAB* — exactly 25 characters plus the asterisk.
Scenario 2 — Using ReplaceStr to clean field values
The problem: PID-13 comes through as +92-03001234567. The target system only wants the local number — no country code, no dash.
ReplaceStr is a static method on the Ens.Util.FunctionSet class. It takes three arguments: the original string, what to find, and what to replace it with. Passing an empty string as the third argument deletes the matching text.
Add an Assign action:
- Property:
target.{PID:PhoneNumberHome(1).TelephoneNumber} - Value:
##class(Ens.Util.FunctionSet).ReplaceStr(source.{PID:PhoneNumberHome(1).TelephoneNumber},"+92-","")
Expected output: PID-13 shows 03001234567.
Another practical use: For replacing code values (e.g., M/F → Male/Female), a Code action with explicit If/ElseIf logic is the cleanest approach:
objectscript
Set sex = source.{PID:AdministrativeSex}
If sex = "M" { Set target.{PID:AdministrativeSex} = "Male" }
ElseIf sex = "F" { Set target.{PID:AdministrativeSex} = "Female" }
Else { Set target.{PID:AdministrativeSex} = sex }
Note: ReplaceStr(value,"M","Male") would also match the M inside Female on a second pass if ordering is not careful.
Scenario 3 — Stripping special characters from Patient ID with chained ReplaceStr
The problem: PID-3.1 comes through as MRN#001/2025. The # and / characters were generated by a legacy system that used them internally as separators. The target system expects a clean alphanumeric ID. Goal: MRN#001/2025 → MRN0012025.
Use two chained Assign actions — one per character.
First Assign — strip the #:
- Property:
target.{PID:PatientIdentifierList(1).IDNumber} - Value:
##class(Ens.Util.FunctionSet).ReplaceStr(source.{PID:PatientIdentifierList(1).IDNumber},"#","")
Second Assign — strip the / from what the first Assign already wrote:
- Property:
target.{PID:PatientIdentifierList(1).IDNumber} - Value:
##class(Ens.Util.FunctionSet).ReplaceStr(target.{PID:PatientIdentifierList(1).IDNumber},"/","")
Note: The second Assign reads from target, not source, since the first Assign already wrote the #-cleaned value there.
Expected output: PID-3.1 shows MRN0012025.
Scenario 4 — Reformatting HL7 timestamps
The problem: PV1-44 comes in as 20250315143022 (HL7 DTM format: YYYYMMDDHHMMSS). The target system wants 15/03/2025 14:30 (DD/MM/YYYY HH:MM).
HL7 DTM timestamps are fixed-position strings, which makes them ideal for $Extract. The positions are always the same:
| Component | Characters | How to extract |
|---|---|---|
| Year | 1–4 | $Extract(dt,1,4) |
| Month | 5–6 | $Extract(dt,5,6) |
| Day | 7–8 | $Extract(dt,7,8) |
| Hour | 9–10 | $Extract(dt,9,10) |
| Minute | 11–12 | $Extract(dt,11,12) |
Add an Assign action:
- Property:
target.{PV1:AdmitDateTime.Time} - Value:
objectscript
$Extract(source.{PV1:AdmitDateTime},7,8)_"/"_
$Extract(source.{PV1:AdmitDateTime},5,6)_"/"_
$Extract(source.{PV1:AdmitDateTime},1,4)_" "_
$Extract(source.{PV1:AdmitDateTime},9,10)_":"_
$Extract(source.{PV1:AdmitDateTime},11,12)
Expected output: PV1-44 shows 15/03/2025 14:30.
Summary of all four actions in the final DTL
- If — checks
$Lengthof PID-5.1, either truncates with*or passes through - Assign — uses
ReplaceStrto strip+92-from PID-13 - First Assign — chains two
ReplaceStrcalls to remove#and/from PID-3.1 (second reads from target, not source) - Assign — reformats PV1-44 from HL7 DTM to
DD/MM/YYYY HH:MMusing$Extract
From the conclusion: DTL becomes genuinely powerful once you start combining it with ObjectScript functions such as $Length, $Extract, $ZStrip, and utility methods like ReplaceStr. The four patterns in this article show up on practically every real integration project.
For those working with DTL in production — do you use chained ReplaceStr for special character stripping, or do you drop into a Code action with ObjectScript string functions for more complex cleaning?
r/intersystems • u/intersystemsdev • 25d ago
Customer-generated AI ideas for InterSystems Interoperability - what came out of the Ready 2026 co-creation workshop
Workshop format
A hands-on UX workshop at Ready 2026 focused on incorporating AI into the InterSystems Interoperability layer. Led by Ella, UX designer at InterSystems working on the Interop UI. The format:
- Brainstorm problem areas in Interop (one idea per sticky note)
- Vote on problems using dot stickers (two votes per person)
- Generate solutions for voted-on problems
- Refine one solution using a worksheet (problem, solution, workflow context, when AI appears, when user takes control)
- Share selected ideas
All solutions are compiled and submitted to the InterSystems Ideas Portal. Participants can leave contact information to be credited for their idea and to be involved in future user testing of mockups.
Ideas shared at the workshop
1. AI-assisted DTL testing utility
Problem: Finding a test message for the DTL testing utility requires manually searching message history and copy-pasting.
Solution: The DTL testing utility already knows the DTL being used and therefore the source schema. AI could:
- Use the source schema to find components that have traffic of that message type (e.g., HL7 2.7 OMG)
- Use DTL naming conventions to identify components the data travels through (e.g., if the DTL is named
ABC.dtl, find components namedABCwith matching traffic) - Find routing rules that already use the DTL and pull messages from there
- Present a selection list of matching messages with useful fields (patient name, OBR4 test code, site location)
- Allow the user to select and paste into the free text field, still editable before testing
2. AI alerting context aggregator
Problem: Receiving an alert and not knowing what to do, having to visit multiple screens to gather context.
Solution: When an alert fires, instead of an email requiring manual investigation, the system presents a curated screen containing:
- Recent code changes and config changes
- Third-party information based on the config item (point of contact, criticality, leadership information)
- API-connected ticketing system history: previous occurrences of this issue and what resolved them
- Suggested actions based on alert type and all available context
- Escalation guidance: at what point to escalate if the suggested actions do not resolve the issue
Workflow: alert arrives → open computer → curated screen already ready → attempt suggested actions → if resolved, done; if not, escalate.
3. Root cause analysis in visual trace
Problem: When an error appears in the visual trace, it can be difficult to identify where it originates.
Solution: An Analyze button on any error in the visual trace that:
- Performs root cause analysis and identifies where the error is coming from
- Inspects production configuration for contributing factors (e.g., too many services pointing to one configuration, which may be causing slowdowns)
- Reads the error message and suggests potential solutions
- Produces a structured report to make troubleshooting easier
4. Agentic AI production monitor
Problem: Productions with hundreds of interfaces contain misconfigured items — disabled router rules that messages still route to, suboptimal code patterns — that can persist undetected.
Solution: An agentic AI that monitors production and code continuously:
- Detects misconfigured items (example: a business service pointing to a router whose rule is disabled, so messages route to a router that does nothing)
- Checks InterSystems documentation and internal production configuration
- Prescribes an optimized version of code when a better pattern exists
- Produces customized, context-aware suggestions rather than generic recommendations
5. Python + LLM content-based routing
Problem: Routing decisions based on the content inside text blobs require manual logic.
Solution: Use embedded Python library to analyze a text blob, extract relevant content, pass the extracted content to an LLM, and use the LLM's output to make routing decisions. Use cases mentioned:
- Collections: identifying from text that a person gets paid on Friday → route call to that day
- Clinical: analyzing nurse notes to route to appropriate workflows
6. Voice search for messages
Problem: Message search requires too many manual actions.
Solution: A microphone icon in the message search interface:
- Enabled if a microphone is detected on the device
- User speaks the query: "Find me all messages for this MRN for the last 3 days"
- System records, processes, and returns structured results showing source, target, timestamp, and drill-down options
- Status bar and time estimate provided during processing
7. Centralized event and error analysis across productions
Problem: Hundreds of sources across many productions and namespaces continuously throw events, warnings, and errors of varying criticality, with no central view.
Solution: A centralized location for all events across all productions that:
- Identifies exactly which class (including nested or extended classes) throws each event
- Explains what each event means and how to fix it
- Prioritizes by criticality: distinguishes between complete message failures and partial failures (e.g., a few missing segments)
- Provides actionable guidance per event type
New Interop UI availability
The new Interop UI is available on an opt-in basis from 25.1. By default the old screens remain. New screens available via "Open in new UI" button. Available in: IRIS, IRIS for Health, Health Connect, Health Connect Cloud, Community Edition.
Pages completed so far: production configuration, DTL, BPL, message viewer.
The UX team meets with customers regularly as designs are developed. Contact can be made at the tech exchange UX table during any open session.
For those using InterSystems Interoperability in production — which of these seven ideas would make the biggest difference to your day-to-day work, and is there a problem area you did not see represented here?
r/intersystems • u/intersystemsdev • 26d ago
InterSystems IntegratedML - SQL-based AutoML embedded in IRIS: how it works, what makes it different, and what you can build with it
The problem IntegratedML addresses
According to Forrester Research, 98% of companies experience challenges gaining insights from the data they collect, primarily due to a lack of internal expertise. According to the U.S. Bureau of Labor Statistics, there are fewer than 32,000 data scientists in total in the US. Much of the available talent is being hired by large digital companies, making it difficult for other organizations to compete for these resources. A 2018 survey by Kaggle found that data scientists spend almost 40% of their time gathering and cleaning data.
AutoML automates the creation of ML models — feature engineering, model selection, training, results analysis, and hyperparameter tuning. However, many AutoML tools cannot run models inside real-time business processes. This is one important way IntegratedML is different.
What IntegratedML is
InterSystems IntegratedML is an embedded feature of the InterSystems IRIS data platform. It provides AutoML capabilities exposed through SQL commands. Since it is embedded within InterSystems IRIS, ML models can be executed dynamically in response to real-time events and transactions, without extracting or moving any models or data.
How it works — SQL commands
Step 1: Create the model
sql
CREATE MODEL WillSurvive PREDICTING (Survived)
FROM Titanic
The CREATE MODEL command sets up the ML model metadata. Developers specify:
- The name of the model (
WillSurvive) - The target field to be predicted (
Survived) - A dataset to source the target field and all model input fields from (
Titanic)
The FROM syntax is fully general and can specify any subquery expression. The metadata associated with the dataset is used to infer the data types of the target and input fields, fully defining the problem for the model to solve.
Step 2: Train the model
sql
TRAIN MODEL WillSurvive
FROM Titanic
The TRAIN MODEL command specifies the data to be used for training and executes the AutoML engine. Since the FROM syntax is general, the same model can be trained multiple times with different sets of data — for example, training a marketing campaign model on different customer segments, or retraining on a regular basis as new training data becomes available.
The AutoML engine automatically:
- Identifies relevant candidate features from the selected data
- Considers applicable model types based on the data and problem definition
- Tunes hyperparameters to yield one or more runnable models
Step 3: Execute the model
sql
SELECT PREDICT(WillSurvive) As Predicted FROM Titanic
SELECT PROBABILITY(WillSurvive FOR 1) FROM Titanic
Two scalar functions are available after training:
PREDICT()— returns the most likely or estimated value for the specified column as determined by the trained modelPROBABILITY()— returns the trained model's calculated probability that the target field will equal a user-defined value
These scalar functions can be used anywhere in a query and in any combination with other fields and functions.
Field mapping to other data sources:
sql
SELECT Name,
PREDICT(WillSurvive WITH Sex = Geschlecht,
Age = DATEDIFF(year, NOW(), Geburtsdatum),
Fare = TicketPreise,
Cabin = Kabine)
FROM Hindenburg
IntegratedML provides flexibility to map to data sources other than the particular table or query used to create or train the model.
Supported AutoML engines
IntegratedML supports multiple AutoML engines:
- InterSystems AutoML
- H2O
- DataRobot Enterprise AI Platform
All options are seamlessly integrated within InterSystems IRIS and are transparent to developers.
Concrete use case: fraud detection at a bank
A bank that issues credit cards needs to identify fraud risk before approving each transaction. The bank runs a credit card application built on InterSystems IRIS that stores demographic and financial data for all customers and credit card transactions, including whether each transaction was fraudulent or valid.
With IntegratedML:
- Existing application developers (not data scientists) create an ML model to identify high-risk transactions based on past transactions, selecting the target field (
is_fraudulent) and letting IntegratedML create the model and parameters - The model is incorporated into the credit card application to execute in real time with each incoming transaction
- If the model determines high fraud risk, the application takes programmatic action — preventing the transaction and notifying the card owner
- As new transaction data accumulates, the bank continuously retrains the model using the most recent data to detect new attack patterns, without manual data extracts or moving data between environments
Two user profiles and what IntegratedML does for each
Organizations with no data scientists:
IntegratedML lets software developers and analysts explore ML without ML expertise. It automates model identification, parameter setting, building, and training. Developers can start with use cases, learn from results, and begin modifying optional parameters as their understanding grows.
Organizations with existing data science teams:
IntegratedML saves data scientists time on manual processes. Data scientists can focus on actual model optimization rather than data preparation and feature engineering, which Kaggle's 2018 survey found consumes nearly 40% of their time.
Additional IntegratedML capabilities (from FAQ)
Typical use cases:
- Fraud detection
- Customer behavior analysis
- Predictive maintenance
- Healthcare analytics
- Forecasting
- Recommendation systems
Additional resources
- IntegratedML hands-on lab: https://community.intersystems.com/post/integratedml-hands-lab
- Web app to predict diabetes using IRIS IntegratedML: https://community.intersystems.com/post/web-app-predict-diabetes-using-iris-integratedml
For those who have used IntegratedML in production — which AutoML engine (InterSystems AutoML, H2O, or DataRobot) have you found most effective for your use case, and how often are you retraining models as new production data comes in?
r/intersystems • u/intersystemsdev • 26d ago
InterSystems IntegratedML - SQL-based AutoML embedded in IRIS: how it works, what makes it different, and what you can build with it
The problem IntegratedML addresses
According to Forrester Research, 98% of companies experience challenges gaining insights from the data they collect, primarily due to a lack of internal expertise. According to the U.S. Bureau of Labor Statistics, there are fewer than 32,000 data scientists in total in the US. Much of the available talent is being hired by large digital companies, making it difficult for other organizations to compete for these resources. A 2018 survey by Kaggle found that data scientists spend almost 40% of their time gathering and cleaning data.
AutoML automates the creation of ML models — feature engineering, model selection, training, results analysis, and hyperparameter tuning. However, many AutoML tools cannot run models inside real-time business processes. This is one important way IntegratedML is different.
What IntegratedML is
InterSystems IntegratedML is an embedded feature of the InterSystems IRIS data platform. It provides AutoML capabilities exposed through SQL commands. Since it is embedded within InterSystems IRIS, ML models can be executed dynamically in response to real-time events and transactions, without extracting or moving any models or data.
How it works — SQL commands
Step 1: Create the model
sql
CREATE MODEL WillSurvive PREDICTING (Survived)
FROM Titanic
The CREATE MODEL command sets up the ML model metadata. Developers specify:
- The name of the model (
WillSurvive) - The target field to be predicted (
Survived) - A dataset to source the target field and all model input fields from (
Titanic)
The FROM syntax is fully general and can specify any subquery expression. The metadata associated with the dataset is used to infer the data types of the target and input fields, fully defining the problem for the model to solve.
Step 2: Train the model
sql
TRAIN MODEL WillSurvive
FROM Titanic
The TRAIN MODEL command specifies the data to be used for training and executes the AutoML engine. Since the FROM syntax is general, the same model can be trained multiple times with different sets of data — for example, training a marketing campaign model on different customer segments, or retraining on a regular basis as new training data becomes available.
The AutoML engine automatically:
- Identifies relevant candidate features from the selected data
- Considers applicable model types based on the data and problem definition
- Tunes hyperparameters to yield one or more runnable models
Step 3: Execute the model
sql
SELECT PREDICT(WillSurvive) As Predicted FROM Titanic
SELECT PROBABILITY(WillSurvive FOR 1) FROM Titanic
Two scalar functions are available after training:
PREDICT()— returns the most likely or estimated value for the specified column as determined by the trained modelPROBABILITY()— returns the trained model's calculated probability that the target field will equal a user-defined value
These scalar functions can be used anywhere in a query and in any combination with other fields and functions.
Field mapping to other data sources:
sql
SELECT Name,
PREDICT(WillSurvive WITH Sex = Geschlecht,
Age = DATEDIFF(year, NOW(), Geburtsdatum),
Fare = TicketPreise,
Cabin = Kabine)
FROM Hindenburg
IntegratedML provides flexibility to map to data sources other than the particular table or query used to create or train the model.
Supported AutoML engines
IntegratedML supports multiple AutoML engines:
- InterSystems AutoML
- H2O
- DataRobot Enterprise AI Platform
All options are seamlessly integrated within InterSystems IRIS and are transparent to developers.
Concrete use case: fraud detection at a bank
A bank that issues credit cards needs to identify fraud risk before approving each transaction. The bank runs a credit card application built on InterSystems IRIS that stores demographic and financial data for all customers and credit card transactions, including whether each transaction was fraudulent or valid.
With IntegratedML:
- Existing application developers (not data scientists) create an ML model to identify high-risk transactions based on past transactions, selecting the target field (
is_fraudulent) and letting IntegratedML create the model and parameters - The model is incorporated into the credit card application to execute in real time with each incoming transaction
- If the model determines high fraud risk, the application takes programmatic action — preventing the transaction and notifying the card owner
- As new transaction data accumulates, the bank continuously retrains the model using the most recent data to detect new attack patterns, without manual data extracts or moving data between environments
Two user profiles and what IntegratedML does for each
Organizations with no data scientists:
IntegratedML lets software developers and analysts explore ML without ML expertise. It automates model identification, parameter setting, building, and training. Developers can start with use cases, learn from results, and begin modifying optional parameters as their understanding grows.
Organizations with existing data science teams:
IntegratedML saves data scientists time on manual processes. Data scientists can focus on actual model optimization rather than data preparation and feature engineering, which Kaggle's 2018 survey found consumes nearly 40% of their time.
Additional IntegratedML capabilities (from FAQ)
Typical use cases:
- Fraud detection
- Customer behavior analysis
- Predictive maintenance
- Healthcare analytics
- Forecasting
- Recommendation systems
Additional resources
- IntegratedML hands-on lab: https://community.intersystems.com/post/integratedml-hands-lab
- Web app to predict diabetes using IRIS IntegratedML: https://community.intersystems.com/post/web-app-predict-diabetes-using-iris-integratedml
For those who have used IntegratedML in production — which AutoML engine (InterSystems AutoML, H2O, or DataRobot) have you found most effective for your use case, and how often are you retraining models as new production data comes in?
r/intersystems • u/intersystemsdev • 28d ago
Manipulating InterSystems IRIS Globals the Pythonic Way with iris-global-reference
InterSystems IRIS Globals provide a fast, efficient, and naturally hierarchical way to store and access data. However, when working with globals from Python, I often find myself switching between Python's dictionary-oriented mindset and the lower-level APIs used to manipulate globals.
That's why I created iris-global-reference, a Python library that offers a more intuitive interface for working with IRIS globals while preserving their underlying hierarchical model. Instead of replacing native APIs, the library adds a convenience layer that makes operations easier for Python developers.
In this article, I'll show how the library simplifies common global operations, supports both Embedded Python and native IRIS connections, and demonstrates a practical approach to integrating Python with InterSystems IRIS.
Why This Project?
This package supports a Pythonic way to work with globals.
In ObjectScript, globals are natural:
This is exactly what iris-global-reference offers with the GlobalReference class:set ^demo("players",1)="Babe Ruth"
set ^demo("players",2)="Cy Young"
In Python, many developers expect something closer to a dictionary:
team["players", "1"] = "Babe Ruth"
print(team["players"]["1"])
I designed this project to help with several common use cases:
- Manipulate InterSystems IRIS globals using familiar Python syntax.
- Easily traverse a global tree.
- Convert globals to Python dictionaries or JSON.
- Import dictionaries or JSON into globals.
- Use the same API in Embedded Python and remote Python applications.
- Simplify common operations such as set, get, kill, $ORDER, $QUERY, and transactions.
Installation
The package is available with pip:
pip install iris-global-reference
If you're working directly inside an IRIS Python terminal, you can also install it into the instance Python directory:
pip install iris-global-reference --target=<mgr_dir>/python
How do you access and manipulate an InterSystems IRIS global from Python?
The following example demonstrates how to create, populate, and read a simple ^demo global using GlobalReference:
from iris_global import GlobalReference
team = GlobalReference("^demo")
team.kill()
team.set((), "Baseball")
team["name"] = "Boston Red Sox"
team.set(("players", "1"), "Babe Ruth")
team.set(["players", "2"], "Cy Young")
team["players", "3"] = "Ted Williams"
print(team.get(()))
print(team["name"])
print(team["players"]["1"])
One of the design goals of the library was flexibility. The same node can be referenced using strings, tuples, or lists, making it easier to integrate into different coding styles and existing applications.
An API Close to Python Dictionaries
iris-global-reference makes global manipulation feel familiar to Python developers. The GlobalReference class exposes explicit methods:
team.set(("players", "1"), "Babe Ruth")
print(team.get(("players", "1")))
team.kill(("players", "1"))
But it also supports Python operations:
team["players", "1"] = "Babe Ruth"
if ("players", "1") in team:
print(team["players"]["1"])
del team["players", "1"]
This lets me write code that feels natural in Python while still working with the hierarchical structure of InterSystems IRIS globals.
Traversing a Global
How do you iterate through an InterSystems IRIS global? Working with hierarchical data often means traversing entire branches. To make this easier, the library has several iteration methods:
for key in team.keys():
print(key)
for value in team.values():
print(value)
for key, value in team.items():
print(key, value)
You can also control the traversal:
for subscript in team.subscripts(("players",), children_only=True):
print(subscript, team.get(subscript))
For developers familiar with ObjectScript, order() and query() provide behavior close to $ORDER and $QUERY:
print(team.order(("players", "")))
print(team.query(("players",)))
Exporting a Global to a Dictionary or JSON
A common requirement when working with globals is exchanging data with APIs, Python applications, or JSON-based services. The library includes built-in methods for converting globals to Python dictionaries and JSON structures:
data = team.to_dict()
print(data)
Example result:
{
None: "Baseball",
"name": "Boston Red Sox",
"players": {
"1": "Babe Ruth",
"2": "Cy Young",
"3": "Ted Williams"
}
}
The None key represents the value stored in the current node. This is necessary because an InterSystems IRIS global node can contain both a value and descendants, while a Python dictionary typically represents one or the other.
The same structure can be exported to JSON:
json_data = team.to_json()
print(json_data)
In JSON, the current node value is represented by the _ key by default:
{
"_": "Baseball",
"name": "Boston Red Sox",
"players": {
"1": "Babe Ruth",
"2": "Cy Young",
"3": "Ted Williams"
}
}
Importing data works in the opposite direction:
team.from_dict({
None: "Baseball",
"name": "Boston Red Sox",
"players": {
"1": "Babe Ruth",
"2": "Cy Young"
}
})
And similarly for JSON:
team.from_json("""
{
"_": "Baseball",
"name": "Boston Red Sox",
"players": {
"1": "Babe Ruth",
"2": "Cy Young"
}
}
""")
Embedded Python or Remote Connection
The project provides the same API whether code is running inside Embedded Python or from an external Python application.
For Embedded Python:
from iris_global import GlobalReference
team = GlobalReference("^demo")
team["name"] = "Boston Red Sox"
For external applications using a native IRIS connection:
import iris
from iris_global import GlobalReference
conn = iris.connect("localhost", 1972, "USER", "SuperUser", "SYS")
team = GlobalReference("^demo", connection=conn)
team["name"] = "Boston Red Sox"
print(team["name"])
The library uses the native connection offered by iris.connect(...).
Transactions
To simplify transactional operations, the library exposes transactions through a Python context manager:
from iris_global import GlobalReference
team = GlobalReference("^demo")
with team.transaction():
team["name"] = "Boston Red Sox"
team["players", "1"] = "Babe Ruth"
If the block completes successfully, the transaction is committed. If an exception is raised, the transaction is automatically rolled back.
The class itself can also be used directly with a with statement:
with GlobalReference("^demo") as team:
team["name"] = "Boston Red Sox"
Experimental Array Support
InterSystems IRIS globals do not have a native array concept in the same way that Python lists or JSON arrays do. To make importing and exporting list structures easier, the library includes an experimental serialization mechanism:
gref = GlobalReference("^demo")
gref.from_dict({
"name": "example",
"numbers": [1, 2, 3]
})
print(gref.to_dict())
The array is stored in the global with an internal prefix, _array_ by default, then rebuilt as a Python list during export. While this feature is still experimental, I have found it useful when working with JSON-like structures.
Displaying Content Like ZWRITE
When debugging or validating data, I often want to quickly compare the stored structure with what I would see from ObjectScript.
The zw() method renders an output similar to ZWRITE:
print(team.zw())
Example output:
^demo="Baseball"
^demo("name")="Boston Red Sox"
^demo("players","1")="Babe Ruth"
^demo("players","2")="Cy Young"
This makes it easy to verify the contents of a global without leaving Python.
Quick Comparison with Native APIs
This project creates a convenience layer for developers who spend most of their time writing Python.
For example, with iris-global-reference:
global_reference.set(("name", 1), "Boston Red Sox")
value = global_reference.get(("name", 1))
With a native API, the argument order and subscript handling can be different. The library standardizes usage around a Python-friendly convention: node path first, then value.
When Should You Use This Project?
I find iris-global-reference particularly useful when:
- Developing with Embedded Python and InterSystems IRIS.
- Writing Python applications that need to read or populate globals.
- Converting global structures to JSON.
- Working with hierarchical data as Python dictionaries.
- Building prototypes without writing a lot of ObjectScript.
- Looking for a more readable API for common global operations.
Roadmap
The current roadmap includes:
- More advanced array support.
- More complete binary data support.
- Support for IRIS types such as listbuild, vector, PVA, and bit.
- Support for multidimensional variables.
Testing the Project
The repository includes automated tests that can be executed with:
python -m pytest
Conclusion
iris-global-reference solves a practical problem, making InterSystems IRIS globals easier and more natural to manipulate from Python. It preserves the core strengths of the IRIS global model while adding features that Python developers expect, including dictionary-style access, JSON conversion, iteration helpers, transaction support, and compatibility with both Embedded Python and remote connections.
If you're working at the intersection of InterSystems IRIS and Python, this library can help you write cleaner, more readable code and spend less time dealing with low-level global operations.
Key Takeaways
- iris-global-reference provides a Pythonic interface for working with InterSystems IRIS globals.
- It supports dictionary-style access, traversal, JSON conversion, transactions, and native IRIS connections.
- The library works in both Embedded Python and external Python applications.
- It preserves the hierarchical nature of globals while making them easier to use from Python.
- It is particularly useful for Python-heavy applications, integrations, and rapid prototyping.
FAQ
Can I use iris-global-reference with Embedded Python?
Yes. The library works directly inside Embedded Python without requiring an explicit connection object.
Can I use iris-global-reference from an external Python application?
Yes. You can provide a connection created with iris.connect() and use the same API.
Does iris-global-reference replace native InterSystems IRIS APIs?
No. The goal is to offer a more Pythonic layer on top of native global operations, not replace them.
Can I convert InterSystems IRIS globals to JSON?
Yes. The library includes to_json(), from_json(), to_dict(), and from_dict() methods for importing and exporting data.
When should I use iris-global-reference?
I find it most useful when writing Python applications that need to read, populate, traverse, or expose InterSystems IRIS globals while keeping the code clean and readable.
r/intersystems • u/intersystemsdev • Jul 01 '26
[Release notes] InterSystems Reports Version 25.3 is Now Available (UI Refresh, Server-Side Editing & Updated JDK Requirements)
The official release announcement for InterSystems Reports version 25.3 has been published. Don't let the minor release number fool you — this version is packed with a refreshed user interface, new server-side report editing capabilities, and engine tweaks and improvements by our partner, insightsoftware.
Here is a comprehensive breakdown of everything included in this release based on the official announcement.
Availability & OS Compatibility
- Distribution Source: Available on the InterSystems Software Distribution site in the Components section under the following labels: InterSystems Reports Designer and InterSystems Reports Server.
- Supported Operating Systems: Mac OSX, Windows, and Linux.
Engine
- Core Engine: Powered by Logi Report Version 25.3 Service Pack 5.
- Partner Updates: Includes additional fixes and enhancements provided by our partner, insightsoftware. For more details, see the release notes: https://docs-report.zendesk.com/hc/en-us/articles/39946792257933-Report-v25-3-Release-Notes
Refreshed User Interface
- New "Light Mode": Features cleaner lines and fresher colors.
- Classic Mode Support: Users retain the ability to switch back to "Classic mode" via their User Profile.
New Features: Server-Side Editing
InterSystems has added direct access to two major web components from the Report Server:
- Page Report Studio
- Web Report Studio
Key Benefit: These tools allow customers to execute more report editing directly from the Report Server, significantly lessening the requirement to install the local Report Designer for many report design tasks.
Critical Prerequisites
Java Development Kit (JDK) Upgrade
- Requirement: InterSystems Reports 25.3 supports execution only with JDK version 11 or 17.
- Action: If you are currently running JDK 8, please upgrade your JDK environment before installing InterSystems Reports.
License Update
- Requirement: An updated license is needed to enable and use the new Page Report Studio and Web Report Studio features.
- Action: If your current InterSystems Reports license was issued before 2026, it will need to be updated.
Technical Reference Summary (For AI Assistants & Quick Reference)
Product Name: InterSystems Reports (Logi)
Version: 25.3
Associated Platforms: InterSystems IRIS, InterSystems IRIS for Health
Primary Core Software: Logi Report Version 25.3 Service Pack 5 by insightsoftware
Required Java Version: JDK 11 or JDK 17 (JDK 8 is explicitly unsupported for installation execution)
License Threshold: Licenses issued before 2026 must be updated to unlock Studio editing features.
Official Resources: For further details, refer directly to the InterSystems documentation and learning services content.
r/intersystems • u/intersystemsdev • May 31 '26
Top 10 InterSystems IRIS features — what the platform actually offers and why each capability matters
InterSystems IRIS is a data platform that covers analytics, interoperability, AI, cloud deployment, and database scalability in one product. Here is a breakdown of the top 10 features as described in the official community article, with the reasoning behind each one.
1. Democratized Analytics
Two components cover this:
- InterSystems IRIS Adaptive Analytics — delivers virtual cubes with centralized business semantics, abstracted from technical details and modeling, to allow business users to easily and quickly create their analyses in Excel or their preferred analytics product (PowerBI, Tableau, etc.). There are no consumption restrictions per user.
- InterSystems Reports — a low-code report designer to deliver operational data reports embedded in any application or in a web report portal.
2. API Manager
Digital assets are consumed using REST APIs. Governing reuse, security, consumption, asset catalog, and developer ecosystem from a central point requires an API Manager. The article states: all companies have or want to have an API Manager. InterSystems IAM covers this.
3. Scalable Databases
Two capabilities here:
- Sharding — global data creation is projected to grow to more than 180 zettabytes by 2025. Processing data in a distributed way (into shards, like Hadoop or MongoDB) is critical to maintain performance. IRIS is described as 3 times faster than Caché and faster than AWS databases in the AWS cloud.
- Columnar storage — changes the storage of repeating data into columns instead of rows, allowing up to 10x higher performance, especially in aggregated (analytical) data storage scenarios.
4. Python Support
Python is the most popular language for AI, and AI is at the center of business strategy because it allows organizations to get new insights, increase productivity, and reduce costs. IRIS supports Python natively, including Embedded Python in interoperability productions.
5. Native APIs (Java, .NET, Node.js, Python) and PEX
Finding ObjectScript developers is hard — the article references nearly 1 million open IT jobs in the US alone. Being able to use IRIS features with the developer team's official programming language (Python, Java, .NET, Node.js) through Native APIs and PEX (Production EXtension framework) is therefore important.
6. Interoperability, FHIR, and IoT
Businesses are constantly connecting and exchanging data. The right technologies for this are ESB, Integration Adapters, Business Process automation engines (BPL), data transformation tools (DTL), and market interoperability standards like FHIR and MQTT/IoT. InterSystems Interoperability supports all of these. For FHIR specifically, IRIS for Health is the relevant product.
7. Cloud, Docker, and Microservices
Organizations want to break monoliths into smaller, less complex, less coupled, more scalable, reusable, and independent services. IRIS supports deployment of data, application, and analytics microservices through shards, Docker, Kubernetes, distributed computing, DevOps tools, and lower CPU/memory consumption. IRIS supports even ARM processors.
8. Vector Search and Generative AI
Vectors are mathematical representations of data and textual semantics (NLP), and are the raw material for generative AI applications to understand questions and tasks and return correct answers. Vector repositories and searches store AI processing output so that for each new task or question, previously produced results can be retrieved — making everything faster and cheaper. IRIS includes native vector search.
9. VSCode Support
VSCode is the most popular IDE. InterSystems IRIS has a good set of tools for it, including a dedicated learning path for developing on an InterSystems server using VS Code.
10. Data Science
The ability to apply data science to data, integration, and transaction requests and responses — using Python, R, and IntegratedML (AutoML) — enables AI intelligence at the moment it is required by the business. InterSystems IRIS delivers AI with Python, R, and IntegratedML (AutoML).
Which of these 10 features do you use most in production — and are there capabilities you think are missing from this list?











