imio.omnia.core
Shared infrastructure for the Omnia AI-assisted features suite in Plone 6.
This package provides settings management, a shared tabbed control panel, an “AI assistant” content menu, HTTP client services wrapping the Omnia APIs, extensibility interfaces, and branding assets. Other imio.omnia.* packages (imio.omnia.tinymce, imio.omnia.classification, etc.) build on top of it.
Installation
Add the egg to your buildout:
[buildout]
...
eggs =
imio.omnia.core
Then run bin/buildout.
The package is auto-included in Plone via z3c.autoinclude.plugin, so no ZCML slug is needed.
Sub-packages that depend on imio.omnia.core should declare a GenericSetup dependency in their profiles/default/metadata.xml:
<?xml version="1.0"?>
<metadata>
<version>1000</version>
<dependencies>
<dependency>profile-imio.omnia.core:default</dependency>
</dependencies>
</metadata>
Configuration
Registry settings
Stored under the prefix imio.omnia.IOmniaCoreSettings:
Field |
Purpose |
|---|---|
core_api_url |
Omnia Core API base URL |
openai_api_url |
Omnia OpenAI-compatible gateway base URL |
openai_extra_headers |
Additional HTTP headers for the OpenAI-compatible API (dict) |
application_id |
Application identifier (sent as x-imio-application header) |
organization_id |
Default organization / municipality ID (x-imio-municipality) |
enable_proxy |
Enable @@omnia-api proxy endpoint (default: False) |
enable_openai_proxy |
Enable @@omnia-openai-api streaming proxy (default: False) |
api_timeout |
Timeout for outbound HTTP requests, in seconds (default: 30) |
core_auth_type |
Core API authentication: none / oauth2 (default) |
openai_auth_type |
Gateway authentication: none / api_key / oauth2 (default) |
openai_api_key |
Bearer token used when openai_auth_type is api_key |
oauth_grant_type |
password (ROPC, default) or client_credentials |
oauth_client_id |
Keycloak client ID |
oauth_client_secret |
Keycloak client secret |
oauth_token_url |
Token endpoint of the SSO-Apps realm |
oauth_scope |
Optional scope, usually empty |
oauth_client_auth_method |
client_secret_basic (default) or client_secret_post |
oauth_username |
Service-account username (password grant only) |
oauth_password |
Service-account password (password grant only) |
These settings are editable via the Omnia control panel at @@omnia-ai-settings (Site Setup > Omnia).
Authentication
Each service picks its own scheme, so the two APIs can move independently:
core_auth_type — oauth2 (default) or none.
openai_auth_type — oauth2 (default), api_key (sends the static openai_api_key as a Bearer token), or none.
With oauth2, outbound requests carry a Bearer token obtained from the Keycloak SSO-Apps realm. The token is fetched and refreshed by a process-level shared client, so it is acquired once per instance rather than per request. Both grant types are supported: password (ROPC, using oauth_username / oauth_password) and client_credentials.
As a safeguard, setting openai_auth_type to oauth2 while openai_api_url points outside imio.be raises ValueError instead of sending an iMio SSO-Apps token to a third-party provider.
Environment variables
Settings can also be driven by environment variables. They are synced to the Plone registry on Zope startup (requires SITE_ID to locate the Plone site):
Variable |
Registry field |
|---|---|
SITE_ID |
Plone site ID in the ZODB (not stored in registry) |
OMNIA_CORE_API_URL |
core_api_url |
OMNIA_OPENAI_API_URL |
openai_api_url |
OMNIA_OPENAI_API_KEY |
openai_api_key |
OMNIA_APPLICATION_ID |
application_id |
OMNIA_ORGANIZATION_ID |
organization_id |
SSO_APPS_CLIENT_ID |
oauth_client_id |
SSO_APPS_CLIENT_SECRET |
oauth_client_secret |
SSO_APPS_URL |
oauth_token_url |
SSO_APPS_USER_USERNAME |
oauth_username |
SSO_APPS_USER_PASSWORD |
oauth_password |
Set them in buildout.cfg under [instance] environment-vars or export them in your shell before starting Plone.
The SSO_APPS_* names are shared across iMio applications, so one set of variables configures every Omnia-enabled instance of a deployment.
Only free-text settings are configurable this way. The fields backed by a vocabulary — core_auth_type, openai_auth_type, oauth_grant_type, oauth_client_auth_method — keep their defaults (oauth2, password, client_secret_basic) and are changed in the control panel, so a typo in a deployment environment can never persist a value the form would reject.
Extending imio.omnia.core
Adding a control panel tab
Each Omnia sub-package can contribute a tab to the shared control panel. The tabbed layout is rendered by OmniaCoreControlPanelFormWrapper, which reads tabs from portal_actions in the omnia_controlpanel_tabs category.
Step 1 — Define a settings schema and form (browser/controlpanel.py):
from imio.omnia.core.browser.controlpanel import OmniaCoreControlPanelFormWrapper
from plone.app.registry.browser.controlpanel import RegistryEditForm
from plone.z3cform import layout
from zope import schema
from zope.interface import Interface
from my.package import _
class IMySettings(Interface):
my_option = schema.TextLine(
title=_("My option"),
required=False,
)
class MyControlPanelForm(RegistryEditForm):
label = _("My add-on settings")
schema = IMySettings
MyControlPanelView = layout.wrap_form(
MyControlPanelForm, OmniaCoreControlPanelFormWrapper
)
Step 2 — Register the view (browser/configure.zcml):
<browser:page name="my-addon-settings" for="Products.CMFPlone.interfaces.IPloneSiteRoot" class=".controlpanel.MyControlPanelView" permission="cmf.ManagePortal" layer="my.package.interfaces.IMyBrowserLayer" />
Step 3 — Register the tab (profiles/default/actions.xml):
<?xml version="1.0"?>
<object name="portal_actions" meta_type="Plone Actions Tool"
xmlns:i18n="http://xml.zope.org/namespaces/i18n">
<object name="omnia_controlpanel_tabs" meta_type="CMF Action Category">
<object name="my.package" meta_type="CMF Action">
<property name="title" i18n:translate="">My add-on</property>
<property name="url_expr">string:${portal_url}/@@my-addon-settings</property>
<property name="icon_expr">string:gear</property>
</object>
</object>
</object>
Step 4 — Register the settings in the Plone registry (profiles/default/registry/main.xml):
<?xml version="1.0"?>
<registry>
<records interface="my.package.browser.controlpanel.IMySettings"
prefix="my.package.IMySettings" />
</registry>
Using the API services
Two HTTP client services wrap the upstream Omnia APIs. Both are multi-adapters on (context, request) and automatically send x-imio-application and x-imio-municipality headers resolved from the registry and the IOrganizationIDProvider adapter.
IOmniaCoreAPIService
Wraps the Omnia Core AI agents API (/imio/omnia/core/v1/agents/).
Available methods:
expand_text(input, expansion_target=50)
improve_text(input)
reduce_text(input, reduction_target=30)
correct_text(input)
make_accessible(input)
translate_text(input, target_language)
suggest_titles(input)
convert_meeting_notes_to_minutes(meeting_name, meeting_notes)
categorize_content(input, vocabulary, unique=False)
deduce_metadata(input=None, image_url=None, image_file=None)
send(method, path, **kwargs) / post_json(path, payload) for raw calls
Usage:
from zope.component import getMultiAdapter
from imio.omnia.core.interfaces import IOmniaCoreAPIService
service = getMultiAdapter((context, request), IOmniaCoreAPIService)
result = service.improve_text("Le projet va bien.")
# result is a parsed JSON dict from the upstream API
IOmniaOpenAIService
Wraps the Omnia OpenAI-compatible gateway (/imio/omnia/openai/v1/).
Available methods:
list_models()
chat_completions(model, messages, stream=False, temperature=None, max_tokens=None, tools=None, tool_choice=None)
Usage:
from zope.component import getMultiAdapter
from imio.omnia.core.interfaces import IOmniaOpenAIService
service = getMultiAdapter((context, request), IOmniaOpenAIService)
result = service.chat_completions(
model="Mistral Large",
messages=[{"role": "user", "content": "Hello"}],
)
Streaming:
for chunk in service.chat_completions(
model="Mistral Large",
messages=[{"role": "user", "content": "Hello"}],
stream=True,
):
# Each chunk is a parsed JSON dict (SSE data frame)
print(chunk)
Overriding organization ID resolution
The IOrganizationIDProvider adapter resolves which organization / municipality ID is sent with every API request. The default implementation reads from the registry (the organization_id setting).
To override the resolution for specific content types, register a more specific adapter:
from zope.component import adapter
from zope.interface import implementer
from imio.omnia.core.interfaces import IOrganizationIDProvider
from my.package.interfaces import IMyContentType
@adapter(IMyContentType)
@implementer(IOrganizationIDProvider)
class MyOrganizationIDProvider:
def __init__(self, context):
self.context = context
def __call__(self):
# Resolve the org ID from the content hierarchy
return self.context.municipality_code
Register in configure.zcml:
<adapter factory=".adapters.MyOrganizationIDProvider" />
Zope’s adapter specificity ensures your adapter is used for IMyContentType objects while the default adapter handles everything else.
Using the proxy view (@@omnia-api)
The @@omnia-api view forwards browser JavaScript requests to the Omnia Core API, adding authentication headers server-side. This avoids exposing API credentials to the browser.
Requirements:
The enable_proxy setting must be True (disabled by default).
The caller must have the imio.omnia.core: Access Omnia API proxy permission (granted to Authenticated by default).
Requests must be POST with a JSON body.
URL pattern: <context_url>/@@omnia-api/<path> where <path> maps to the upstream API path (e.g. /v1/agents/improve-text).
JavaScript example:
const response = await fetch(
`${portalUrl}/@@omnia-api/v1/agents/improve-text`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ input: selectedText }),
}
);
const data = await response.json();
Using the streaming proxy (@@omnia-openai-api)
The @@omnia-openai-api view proxies browser requests to the OpenAI-compatible gateway and streams the response back as Server-Sent Events (SSE). This lets browser-side chat widgets stream completions without exposing API credentials or the upstream URL to the client.
Requirements:
The enable_openai_proxy setting must be True (disabled by default).
The caller must have the imio.omnia.core: Access Omnia OpenAI proxy permission (granted to Authenticated by default).
Cross-origin requests are rejected: when an Origin header is present, its host must match the portal’s.
Requests must carry plone.protect’s CSRF token in an X-CSRF-TOKEN header (or an _authenticator parameter); otherwise the proxy answers 403.
No Authorization header is needed: a same-origin fetch() sends the visitor’s session cookie, and Zope checks the permission.
Token generation (server-side, e.g. in a viewlet):
from plone.protect.authenticator import createToken
token = createToken()
# Pass this token to the browser, e.g. in a JS settings object
For anonymous visitors the token is the same for everyone and stays valid until the keyring rotates (on user login, at most daily). It proves the caller loaded a page from the site; it does not replace rate limiting.
Projects that need anonymous access to @@omnia-openai-api can override the default role mapping in their own GenericSetup rolemap.xml by granting the imio.omnia.core: Access Omnia OpenAI proxy permission to Anonymous.
URL pattern: <context_url>/@@omnia-openai-api/<path> where <path> maps to the upstream API path (e.g. chat/completions).
JavaScript example:
const response = await fetch(
`${portalUrl}/@@omnia-openai-api/chat/completions`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRF-TOKEN": token,
},
body: JSON.stringify({
model: "Mistral Large",
messages: [{ role: "user", content: "Hello" }],
stream: true,
}),
}
);
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
for (const line of decoder.decode(value).split("\n")) {
if (!line.startsWith("data: ")) continue;
const data = line.slice(6);
if (data === "[DONE]") break;
const chunk = JSON.parse(data);
process.stdout.write(chunk.choices?.[0]?.delta?.content ?? "");
}
}
Available icons
The following icon names are registered in the Plone icon registry and can be used in icon_expr properties of portal actions:
Icon name |
Description |
|---|---|
omnia.ia.dark |
Omnia IA (dark) |
omnia.ia.light |
Omnia IA (light) |
omnia.monochrome.dark |
Monochrome (dark) |
omnia.monochrome.light |
Monochrome (light) |
omnia.picto.dark |
Pictogram (dark) |
omnia.picto.light |
Pictogram (light) |
omnia.logotype.dark |
Logotype (dark) |
omnia.logotype.light |
Logotype (light) |
omnia.imio.logotype.dark |
iMio logotype (dark) |
omnia.imio.logotype.light |
iMio logotype (light) |
The SVG files are served from ++plone++imio.omnia.core/.
License
The project is licensed under the GPLv2.
Contributors
Antoine Duchêne, antoineduchene@icloud.com
Changelog
1.3 (2026-10-09)
Breaking: replace the custom HMAC Bearer token of @@omnia-openai-api by plone.protect’s CSRF token (X-CSRF-TOKEN header) and remove imio.omnia.core.tokens. [chris-adam]
1.2 (2026-08-11)
Set Plone as the default SITE_ID when not defined. [boulch]
1.1 (2026-08-03)
Fix an instance startup crash loop: when every mapped environment variable already matched the registry, sync_env_to_registry left the connection joined to a transaction (silencing the fingerpointing audit log writes to the registry unconditionally) and ConnectionStateError aborted Zope startup. [duchenean]
1.0 (2026-08-03)
DELIBE-289: Add configurable API timeout. [duchenean]
OIA-241: Authenticate outbound Omnia API calls with Keycloak SSO-Apps OAuth 2.0 (ROPC / client credentials via authlib), configurable in the control panel, with credentials supplied by the cross-application SSO_APPS_* environment variables. OAuth 2.0 is the default scheme for both the Omnia Core API and the OpenAI gateway. [duchenean]
DELIBE-322: Replace httpx with httpx2 (maintained pydantic fork). [duchenean]
Removed the unused Vite/React scaffold from browser/resources. [duchenean]
1.0a2 (2026-04-03)
Updated package description to better reflect its role as shared infrastructure. [duchenean]
1.0a1 (2026-04-03)
Initial release. [duchenean]
Added OmniaCoreAPIService multi-adapter wrapping the Omnia Core API (/imio/omnia/core/v1/agents/): expand, improve, reduce, correct, translate, make accessible, suggest titles, convert meeting notes, categorize content, and extract metadata. [duchenean]
Added OmniaOpenAIService multi-adapter wrapping the OpenAI-compatible Omnia gateway (/imio/omnia/openai/v1/): model listing and chat completions with streaming SSE support. [duchenean]
Added shared Omnia control panel (@@omnia-ai-settings) with tabbed navigation extensible via omnia_controlpanel_tabs portal actions. [duchenean]
Added “AI assistant” content menu with dynamic action collection via IOmniaActionsProvider utilities. [duchenean]
Added IOrganizationIDProvider adapter interface for context-aware organization ID resolution. [duchenean]
Added environment variable to registry sync on Zope startup (OMNIA_CORE_API_URL, OMNIA_OPENAI_API_URL, OMNIA_APPLICATION_ID, OMNIA_ORGANIZATION_ID). [duchenean]
Added HMAC-signed token generation for securing browser-to-proxy communication. [duchenean]
Added IImioOmniaControlPanelFieldProvider interface for extending the control panel schema from downstream packages. [duchenean]
Added Omnia SVG branding icons. [duchenean]
Added i18n support (en, fr). [duchenean]
Metadata
Release files for imio.omnia.core 1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| imio_omnia_core-1.3.tar.gz | 102.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| imio_omnia_core-1.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 216.0 kB
Release files / imio_omnia_core-1.3.tar.gz
| Download URL | imio_omnia_core-1.3.tar.gz |
|---|---|
| Size | 102.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d809525e9dd1cb4b96fb9ff05405fafb71d34072913e426c20eee89d50447b34
|
|
BLAKE2b-256 checksum How to use checksums |
5975612f50f7fc7bc1ae93d56dd8bc7d09af765d200b4d106db56db082f24e64
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.12
|
Release files / imio_omnia_core-1.3-py3-none-any.whl
| Download URL | imio_omnia_core-1.3-py3-none-any.whl |
|---|---|
| Size | 113.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
69e508c245eb9c99c919921e5ff2e46a01b109ab9f4d9efc51806442c6f5c116
|
|
BLAKE2b-256 checksum How to use checksums |
b4d577ef31db3f8b2b3ee59ff600af35c2230a47d9a2c2b120c3bf94bb78e1f5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.12
|