Shared infrastructure for iMio's Omnia AI suite for Plone
Project description
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_api_key |
Optional Bearer token sent to the OpenAI-compatible API |
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) |
These settings are editable via the Omnia control panel at @@omnia-ai-settings (Site Setup > Omnia).
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 |
Set them in buildout.cfg under [instance] environment-vars or export them in your shell before starting Plone.
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).
Requests must carry an HMAC Bearer token generated by imio.omnia.core.tokens.generate_token().
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.
Token generation (server-side, e.g. in a viewlet):
from imio.omnia.core.tokens import generate_token
token = generate_token(context.portal_url())
# Pass this token to the browser as a JS variable
Tokens are HMAC-SHA256 signed using the Plone site keyring and expire after 2 hours. The proxy validates them on every request.
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",
"Authorization": `Bearer ${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.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]
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file imio_omnia_core-1.0a2.tar.gz.
File metadata
- Download URL: imio_omnia_core-1.0a2.tar.gz
- Upload date:
- Size: 135.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6200d1626b43732235e71718f91f406b9882ca735f9f90d131cc09c56713fe68
|
|
| MD5 |
1640e848a7740269c33114a04ab77ecb
|
|
| BLAKE2b-256 |
ec89a8bd9a5f9aeb8ab60415847d911b1fb636fb3e052f4791f2bd6f908f743e
|
File details
Details for the file imio_omnia_core-1.0a2-py3-none-any.whl.
File metadata
- Download URL: imio_omnia_core-1.0a2-py3-none-any.whl
- Upload date:
- Size: 147.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c5cb3bfeea35bdc866a367c3d02fbc77cf50be9ff9783b63af30f897bbf9944f
|
|
| MD5 |
b6cdaf7d64bce1da370ebbeb17a03fb9
|
|
| BLAKE2b-256 |
8e86e276fd812c95379a45605a580d01ee7d16c89e5feb795187ba4e652c60c5
|