Release History
To upgrade first run v26.2_to_v26.3.sql followed by upgrade_packages.sql script attached to the GitHub release.
For new installations follow this guide.
Before you upgrade:
- The migration resets the token totals on historical parent runs.
v26.2_to_v26.3.sqlsetstotal_input_tokensandtotal_output_tokensto 0 on every existingworkflow,handoff, andconversationrow inuc_ai_agent_executions. These rows held the sum of their child runs. The new session totals sum every row, so the old parent values would count the same tokens twice. Profile and orchestrator rows are not touched. If you report on these two columns, keep a copy of the table before you upgrade. - The message-log backfill covers the last turn of each session that stored a result. For each session the migration reconstructs the transcript from the most recent top-level run whose stored result holds the accumulated conversation. A failed final turn stores no result, so the transcript comes from the turn before it and stops there. A session whose turns never stored a result gets no transcript. Backfilled rows carry no
agent_codeattribution. Turns from the upgrade onward are recorded in full.
Breaking change:
- Commit an agent before you execute it in the same session transaction. Execution telemetry is now written in autonomous transactions, so
execute_agentcannot see an agent row that the calling transaction has not committed. Earlier, this combination produced a crypticORA-00060self-deadlock. It now raises a clear error that names the cause. Add aCOMMITaftercreate_agent(and afterchange_status) before the firstexecute_agentcall.
Behavior notes:
- An agent that another agent starts is now a child run. A run started from inside a tool handler, or from a PL/SQL workflow step, joins the session of the run it started from and records that run in
uc_ai_agent_executions.parent_execution_id. It is no longer counted as a turn of the conversation:turn_indexstays empty andturn_counton the session header counts the turns of the caller only. A report that read such a run as a turn shows one turn less for each delegation. An explicitp_session_idorp_parent_exec_idstill wins. iteration_countandtool_calls_counthold their real values. Both columns ofuc_ai_agent_executionswere always 0. A profile or orchestrator run now records the number of tool calls it made, and a workflow run records the number of steps it ran.- Every tool call now carries one more argument. UC AI adds the run context to the arguments JSON of every tool, under the reserved key
_ctx. It holds an empty object when the run carries no context. A handler that reads named keys is not affected. A handler that walks all keys, or that refuses an unknown key, sees the new one. A tool can no longer declare a parameter with the name_ctx. finish_reasonhas the new valueunknown. A provider reason that UC AI does not map came through raw before, and matched none of the documented values. It is nowunknown, and the newprovider_finish_reasonholds the word the provider sent.- xAI reads the reasoning effort now. UC AI sent
reasoning_level, a parameter xAI does not have and ignored without an error. It now sendsreasoning_effort. xAI validates that key, so an effort outside the accepted values returns HTTP 400. - A tool handler reads its arguments at the top level on every provider. This concerns one tool shape: a tool whose schema declares exactly one parameter, and that parameter is an object that holds the real arguments. On some providers the handler got
{"parameters": {"city": "Berlin"}}, and on others it got{"city": "Berlin"}. The same handler therefore read null values on half of the providers. UC AI now removes the outer object once, in the tools layer, before it calls the handler. Such a handler must readcitydirectly and no longer throughparameters. A tool that declares its parameters flat, which is the normal shape, is not affected. DEDICATEDserving mode on OCI needs the OCID of the endpoint. Setuc_ai_oci.g_endpoint_id. Without it the mode raisesORA-20502. Before, UC AI sent a request that Oracle’s own SDK types reject.
Features:
APEX chat plug-in:
- APEX Chat plug-in: an Oracle APEX region plug-in that gives an agent a chat interface. It needs one attribute, the code of the agent. The plug-in runs the agent in a
dbms_schedulerjob and polls for the answer, so the page never waits. It shows the tool calls and the reasoning of the run, keeps the conversation, and gives each user their own. This release adds three attributes: Auto Generate Title, Collect Feedback, and Agent Run Context. The plug-in is not part of the UC AI installation: downloaduc-ai-chat-26.3.zipfrom this release. Put an Agent in APEX builds a page on it in six lessons.
New provider:
- Mistral (
uc_ai.c_provider_mistral): text generation and embeddings against the Mistral platform, with model constants inuc_ai_mistral.
Request customization:
- Custom request-body properties (
uc_ai.g_extra_body): shallow-merge arbitrary provider parameters (e.g.top_p,stop_sequences,service_tier,cache_control) into the outgoing request body for options the SDK does not wrap explicitly. Overrides framework values but protects the conversation-defining core keys (model,messages,input,instructions,system,tools). Also settable via theg_extra_bodyconfig key. See generate_text. - Provider (server-side) tools (
uc_ai.g_provider_tools): enable a provider’s own server-side tools — Anthropic web search, OpenAI Responses API web search, Google search, etc. — by appending raw, provider-native tool definitions to the requesttoolsarray. Executed by the provider, not locally; sent even when local tools are disabled. Also settable via theg_provider_toolsconfig key. See generate_text. - Programmatic Tool Calling / Code Mode (
uc_ai.g_enable_programmatic_tools): the model can write one JavaScript program that orchestrates many tool calls in-database instead of one round-trip per call, returning only the final result (large token savings on data-heavy work). The program runs in aPUREMLE execution context inside an isolated, low-privilege sandbox schema (installed once viascripts/install_ptc_sandbox.sqlfrom a repository checkout, orinstall_ptc_sandbox_complete.sqlattached to this release): it has no SQL access whatsoever — it cannot read data and cannot commit or roll back the caller’s transaction — and reaches tools only through a single gateway that enforces a per-run allow-list, a call budget and thebefore_tool_callhook. Per-toolp_code_mode_access(direct|code|both) controls whether a tool is a normal tool, code-only, or both.create_tool_from_schemadefaults it toboth.merge_tool_from_schemadefaults it to null, which keeps the value an existing tool already has, so a merge script never widens a tool that you narrowed. Works across all providers. Requires Oracle 23ai with MLE JavaScript. - Custom HTTP request headers (
uc_ai.g_extra_headers): send additional headers with every provider request, for example a tenant or trace ID. Set them with the name-indexed global (uc_ai.g_extra_headers('X-Tenant-Id') := 'acme';) or with theg_extra_headersconfig key. UC AI appends them after its own headers, so avoid names the framework already sets. See generate_text. - A call carries its own configuration (
p_config): UC AI no longer reads the package globals while a call runs. It takes one snapshot of the configuration at the start, and threads that snapshot through the provider and every nested call.uc_ai.generate_textanduc_ai.generate_embeddingstherefore have new overloads that take the snapshot directly, as a JSON object. Such a call neither reads nor writes the globals, so a library can call UC AI without touching the session state of its caller. A key thatp_configomits falls back to the framework default and not to the current global value.
Results:
- Three new result properties of
generate_text:block_reasonholds the Google reason for a blocked prompt,provider_finish_reasonholds the word the provider sent when UC AI maps none of its own, anderror_messageholds the message OCI Cohere reports in place of an answer. Each property appears only in the condition that produces it. See the properties reference.
Run context:
- Run context:
execute_agentandgenerate_texttake a newp_run_contextparameter, a JSON object of name/value pairs bound to the run, for example{"document_id":"7"}. UC AI hands these values to every tool in the run under_ctx, so a tool handler can read a value that the model can neither see nor choose. Use it for an agent that must stay bound to one document, one case, or one tenant. - The run context fills prompt placeholders: a
{document_id}placeholder resolves from the run context when the input parameters do not supply it. An input parameter of the same name wins. - Nested runs inherit the context: a workflow step, an orchestrator delegate, and a handoff target all run under the same binding, and none of them can drop it.
- A session keeps its binding: the first turn binds the context to the session, so a follow-up turn does not have to pass it again. A later turn can add a key but cannot change one. A change raises the new error
-20507. - New columns:
uc_ai_agent_executions.run_contextanduc_ai_agent_sessions.run_contextrecord the effective context. - Config-driven calls keep their execution context:
uc_ai_settings.build_from_confignow snapshots the execution context, asbuild_from_globalsdoes. Agenerate_textcall withp_configinside an agent run therefore keeps its hook attribution and its run context.
Conversations:
-
Conversation titles (
uc_ai_agents_api.set_session_title): name a conversation on its session header (uc_ai_agent_sessions.title). Front ends decide how a title is produced — an LLM summary of the first exchange, or the user typing one — the engine never sets it itself. The APEX Chat plug-in fills the column with its Auto Generate Title attribute. Setting an existing title overwrites it, so this doubles as “rename”; passp_created_byto restrict the update to the user who opened the session.list_sessionsnow returnstitle, making that cursor directly usable as a conversation list. -
Conversation feedback (
uc_ai_agents_api.set_session_feedback): record the end user’s verdict on a conversation on its session header (uc_ai_agent_sessions.feedback_ratingup/down, plus an optionalfeedback_commentandfeedback_at). Front ends decide how and whether to ask, and the engine never sets it itself. The APEX Chat plug-in asks with its Collect Feedback attribute, which puts two thumb buttons below the newest answer. An existing rating is overwritten, so this doubles as “change my mind”, and a null rating withdraws the feedback — rating, comment and timestamp are cleared together. An unrecognized rating is normalized to null rather than raising, so a newer front end can never hit the check constraint.list_sessionsnow returns all three columns, so quality can be reported on alongside token cost.Note on
p_created_by(both setters): a session opened from a background job has no APEX session, so its header records the DB user, not the end user. If you passAPP_USERagainst such a session, no row matches and the setter does not write. The setter writes a warning to the log, so you can find the cause. If you authorized the user yourself, passnull. -
Conversation sessions (
uc_ai_agent_sessions): a header row for each conversation, above the execution rows. It holds the root agent, the status of the latest turn, maintainedturn_countandmessage_count, and the summed token usage. Each execution records only the tokens of the LLM calls it made itself, so the session totals never double count a nested sub-agent run. Read the headers withuc_ai_agents_api.list_sessions. See multi-agent systems. -
Full conversation transcript with per-message attribution (
uc_ai_agent_messages): one row for each message content item, inseqorder, written per turn as the delta of new messages. History-window trimming only governs what UC AI sends to the LLM, so the persisted record of each new turn stays complete. Each row carriesagent_code, the agent that produced the message — the sub-agent for wrapper turns, the turn’s own agent otherwise. Read it withuc_ai_agents_api.get_session_messages.
Agent memory:
- Agent memory: an agent can store and read knowledge across conversations. It gets a private virtual filesystem with the root
/memories. Three new tables hold the stores and the files. - The new
MEMORYtool works on every provider: it follows the command set of the Anthropic memory tool, as a normal UC AI function tool. The installer creates the tool row. - One call enables memory for an agent:
uc_ai_memory.enable_for_agentwrites the configuration row and adds the tool tag to the prompt profile of the agent. UC AI then appends the MEMORY PROTOCOL block to the system prompt of each new session. - A scope decides which agents and users share one memory:
agent,user,session,shared,global, orcontext. With the scopecontextan agent keeps one store per run-context value, for example one memory per document. Setp_scope => 'context'andp_context_key => 'document_id'. Name ap_store_codeto let several agents share one store per value. - Caps and housekeeping:
max_file_chars,max_files, and the optionalmax_store_charsbound the growth of a store.uc_ai_memory.expire_filesdeletes the files that nobody used for a number of days. The viewuc_ai_v_memory_filesshows what a store holds.
Multi-Agent Systems:
-
Handoff agents (
c_type_handoff) are now fully implemented with tool-based transfers: the engine registers a temporarytransfer_to_<agent>tool per handoff target, the AI transfers by calling it with a context summary (and optional reason), and the target agent answers the user directly. The hop atmax_handoffsruns without transfer tools and must answer. Results includefinal_agent_code,handoff_count,handoff_trail, andmax_handoffs_reached; transfer tool calls are persisted in the session message log. Handoff orchestration configs are now validated atcreate_agenttime (targets must be existing active profile agents). -
Multi-level handoff routing:
handoff_agentsentries accept an optionalcan_transfer_toarray restricting each agent’s outgoing transfer edges — build hierarchies like triage → product support → product technician where the entry agent never sees the deeper specialists. Without it, every agent may transfer to every other entry (full mesh, unchanged behavior). -
Multi-turn handoff conversations (sticky active agent): handoff agents now accept
p_follow_up_message. A follow-up turn resumes with the agent that answered the previous turn, continuing its conversation history — and it keeps its transfer tools so it can hand off when the topic changes. -
Per-step execution checkpointing: workflow, conversation, and handoff agents now write their state to
uc_ai_agent_executions.current_stateafter every step/turn. Running executions can be monitored from other sessions, and failed or crashed executions keep their last known state for diagnosis (cleared automatically on success).uc_ai_agents_api.get_execution_detailsincludes the checkpoint when present. -
Durable execution telemetry: execution rows are now written in autonomous transactions (like Logger). They are visible immediately to other sessions and survive a rollback of the calling transaction, so failed executions and their token accounting are never lost. See the breaking change above.
-
Input mapping engine rewrite:
{$.path}expressions are now resolved by walking the workflow state natively instead of reserializing and reparsing the full state viaAPEX_JSONfor every expression — significantly faster for large workflows and loops. Mapped values are no longer capped at 32k characters (large step outputs like long LLM messages now flow through mappings intact). Resolution errors raise-20455instead of silently returningNULL; unresolvable paths still substitute an empty string as before. -
Loop workflows now snapshot each iteration’s state explicitly (guards against JSON DOM reference aliasing in
_loop_iteration_state). -
PL/SQL workflow steps (
"step_type": "plsql"): run an inline PL/SQL snippet between AI calls for deterministic post-processing without an LLM call. The step receives the full workflow state as:parametersand stores its return value underoutput_keywith its real JSON type. A returned{"__control__":"stop"}halts the remaining steps. -
File input for agents:
execute_agentacceptsp_files, so profile and orchestrator agents can receive documents and images alongside text. Files attach to the first user message and to follow-up messages. -
Caller environment context on executions:
uc_ai_agent_executionsnow records who started a run and from where —created_by,db_user, the APEX user, session, application and page, plusos_user,host,ip_address,module,action,client_identifier,sid, and anenv_contextJSON snapshot. UC AI takes the snapshot once, at the start of the top-level run, and every nested run shares it. See caller environment context. -
Caller class (
audience): each run and each session also records the class of the caller —publicfor an anonymous APEX visitor,authenticatedfor a logged-in APEX user, anddbfor a database or job session. A username cannot express this distinction, because every anonymous visitor of a public page shares one identity. Session headers that the migration backfills carry noaudiencevalue. -
Recursion guard: agent delegation now has a maximum nesting depth. A circular reference — an agent that delegates back to itself — raises
ORA-20405with a message that names the limit, instead of running until it exhausts a resource. -
Remove an agent and its history:
uc_ai_agents_api.purge_agentdeletes every version of an agent, its runs, the runs started from them, its sessions, the messages of both, and the memory that is only this agent’s. Until now an agent that had run could not be deleted at all, because its history referenced it. A shared or global memory store, a session of another agent, and the prompt profile stay. To keep the history, archive the agent instead. -
check_agent_not_referencedsees two more references: an orchestrator delegate and the first agent of a handoff are now found. Deleting or purging such an agent is refused, as it always was for a workflow step. -
Agent as tool: the new function
uc_ai_agents_api.run_agent_as_toolis the whole handler of a tool that runs another agent. Register the tool withp_function_call => 'return uc_ai_agents_api.run_agent_as_tool(''my_agent'', :parameters);'and every agent, workflow step, andgenerate_textcall can reach that agent as a normal tool. The function gives the tool arguments to the agent, hands it the run context of the caller, joins the session of the caller, and returns a failed run as text for the calling model. -
The orchestrator uses the same handler: a delegate tool is now the same one-line handler, so both patterns behave alike.
-
Register an agent as a tool in one call (
uc_ai_agent_exec_api.register_agent_as_tool): this is the registration step of the orchestrator, and it is documented for the first time. Give it an agent code and a tool tag, and it writes a tool whose handler isrun_agent_as_tooland whose parameters are theinput_schemaof the agent. The signature isregister_agent_as_tool(p_agent_code, p_tool_tag, p_tool_code). It lostp_exec_idandp_session_idin this release, because the session and the parent execution now come from the context of the run. The newp_tool_codenames the tool, and null keeps the generated name<agent_code>_TOOL_<guid>that the orchestrator uses for a tool of one run. Two calls that both leave the name to the function write two tools under one tag, and the calling model then sees the same specialist twice under two names. The function inserts the tool, so a tool code that already exists raisesORA-00001. For a setup script that runs more than once, delete the tool first, or register the tool withuc_ai_tools_api.merge_tool_from_schemaand the one-line handler above.
Prompt profiles:
- Update a profile from its row:
uc_ai_prompt_profiles_api.update_prompt_profilenow takes auc_ai_prompt_profiles%rowtype. Read the row withget_prompt_profile, change the columns that you need, and pass the row back. The columns that you do not touch keep their values. - The two
update_prompt_profileoverloads that take every column are deprecated. They still work. An omitted optional parameter sets its column to null, so a call that passes only a new system prompt deletes the model configuration and the schemas without an error. The package spec marks both overloads withpragma deprecate.
Extension points:
- Execution hooks around an agent run: an optional hook package can implement
before_executionandafter_execution.before_executionruns before any execution row or token spend and can raise to veto the run (fail-closed).after_executionis best-effort — UC AI logs an error there and continues. before_tool_callhook: an optional per-tool-call hook that can veto a single tool call mid-run. The caller and agent context is threaded down to the tool layer, so the hook knows who is calling. This is what the paid guardrails layer uses to enforce rate limits and budgets.augment_system_prompthook: an optional procedure that extends the system prompt after placeholder substitution. It applies to the first turn of profile, orchestrator, and workflow-nested agents; follow-up turns inherit the extended system message through the persisted history. Dispatch is best-effort — on an error UC AI logs it and leaves the prompt unchanged.
Providers:
- OCI: configurable output length and detectable truncation (
uc_ai_oci.g_max_tokens, default 4096). The adapter used a hardcodedmaxTokensof 600 and always reportedfinish_reasonasstop, so a truncated answer looked complete. Truncation is now reported. Also settable with theg_max_tokensOCI config key. - OCI: image and PDF input for the GENERIC chat format.
- New model constants:
uc_ai_anthropic.c_model_claude_5_1_fable(Claude Fable 5.1),uc_ai_openai.c_model_gpt_6_astra(GPT-6 Astra),uc_ai_google.c_model_gemini_3_8_flash(Gemini 3.8 Flash), anduc_ai_mistral.c_model_ministral_3_14banduc_ai_mistral.c_model_zai_glm_5_2on the Mistral platform.uc_ai_utils.get_modelsreturns all five. - Fix:
c_model_gemini_2_0_flash_liteheldgemini-2.0-flash_lite. The underscore is not part of the model id, so the constant named a model that does not exist. It isgemini-2.0-flash-litenow. - OCI: reach a dedicated AI cluster (
uc_ai_oci.g_endpoint_id).DEDICATEDserving mode now sends the OCID of the endpoint and no model id, on the chat path and on the embeddings path.ON_DEMANDis unchanged and stays the default. Also settable with theg_endpoint_idOCI config key and on a prompt profile.
Internal:
- New
uc_ai_settingspackage holding a per-call settings snapshot and run state. Provider implementations no longer read mutable package globals for configuration, and no longer write to global accumulators.generate_textsnapshots the configuration once and threads it through the provider, its internal loop, and the URL and credential resolvers. This fixes state leaking between nested agent runs. - Type-conditional constraints on
uc_ai_agents: a check constraint per agent type now enforces that the row carries the configuration its type needs (uc_ai_agents_profile_ck,_workflow_ck,_orch_ck).uc_ai_agent_executionsalso requirescompleted_atfor a terminal status, and gains anupdated_ataudit column that the trigger maintains on update. Code that inserts into these tables directly must satisfy the constraints. - Escaped state in generated PL/SQL: resolved string values are escaped before UC AI embeds them in a PL/SQL expression (conditions, PL/SQL-expression input mappings,
final_message), so untrusted state — LLM output, tool results, user input — cannot inject PL/SQL. Authors keep wrapping string tokens in quotes, so definitions do not change.
Other:
-
Agent skills for UC AI: the repository now ships nine agent skills that teach a coding agent how to call UC AI — quickstart, tools, reasoning, structured output, prompt profiles, multi-agent, agent memory, file analysis, and event callbacks. They use the
SKILL.mdformat, which Claude Code, Cursor, opencode, Codex and other agents read. Install them with the skills CLI:Terminal window # all nine, into the agents the CLI finds in the projectnpx skills add United-Codes/uc_ai# or one, for one agentnpx skills add United-Codes/uc_ai --skill uc-ai-tools -a claude-codeYou can also copy the directories that you need into
.claude/skills/or.agents/skills/yourself. -
New Oracle network setup guide covering the ACL and wallet configuration that HTTPS calls need.
-
New glossary and API cheatsheet.
Fixes:
- An orchestrator run left
uc_ai.g_enable_toolsanduc_ai.g_tool_tagsset to null. Uninitialized locals were restored over them after every run, so a later call in the same session lost its tool configuration. - A step
conditionin an object form ({"type": ..., "expression": ...}) was read withget_stringand therefore ignored, so the step always ran.create_agentnow rejects anything that is not a string withORA-20503. - Response IDs and media types longer than the old column width raised an error in
uc_ai_responses_api(#51). - A loop workflow that ran no step produced a null
final_message. - Reasoning items were malformed when UC AI replayed conversation history, so a follow-up turn against the OpenAI Responses API failed with
HTTP 400 Missing required parameter: 'input[1].summary'. - Reasoning content was stored as the literal string
null, because Responses API reasoning items carry their text insummary[].textand not in a top-level field. - Duplicate or case-variant tool tags raised
ORA-00001oncreate_tool_from_schemaandmerge_tool_from_schema. Tags are now de-duplicated after lowercasing. - Code mode never activated in a schema with ordinary privileges. The sandbox check read
ALL_OBJECTSfor the runner package body, which anEXECUTEgrant never exposes. - Prompt profiles and agents could not enable code mode:
g_enable_programmatic_toolshad no branch in the configuration reader, so the documented declarative option failed withORA-20503. - Code-mode tool descriptions were cut at 200 characters. For a code-only tool the description is the only documentation the model gets.
- The OCI configuration block rejected
g_max_tokenswithORA-20503. - A loop workflow whose
exit_conditionread a later step’s output raisedORA-20451. The unresolved path collapsed into a malformed expression. - OCI GENERIC assistant messages that carry both
contentandtoolCallslost the tool calls. An emptycontentarray short-circuited tool-call handling, which produced an empty final message and a tool call count of 0. final_messageis now always set, for consistency across providers.- A tool that does not exist now raises a clear error from
uc_ai_tools_api. - OpenRouter and xAI web credential usage is corrected.
- The default Ollama port is corrected to 11434.
Anthropic:
- Reasoning failed on most of the models UC AI ships. The request always sent
thinking: {"type": "enabled"}, andclaude-opus-5,claude-opus-4-8,claude-opus-4-7,claude-sonnet-5andclaude-fable-5reject that shape with HTTP 400. These models now get the adaptive shape with an effort, and pre-4.6 models keep the budget path with the documented minimum of 1024. - Reasoning with neither a budget nor a level sent
budget_tokensas null, which returnedInput should be a valid integer. - A
tool_usewhose input was JSON null raisedORA-30625. - A turn with several text blocks kept only the last block as
final_message, and a turn with no text left the answer of the previous turn standing. - Only the last of several system messages reached the request.
Google:
- Two response shapes that arrive with HTTP 200 crashed the parser with
ORA-30625: a blocked prompt, which carries no candidates, and a candidate with no content. A blocked prompt now reportscontent_filterwith the reason inblock_reason. - Gemini 3 takes
thinkingLeveland notthinkingBudget, and the package had no model branch, so a reasoning level had no effect there. An explicit budget stays authoritative and wins. - A reasoning level outside
low,mediumandhighraisedORA-06502. - A multi-part answer kept only its last part as
final_message, and an empty trailing part could blank it.
OpenAI and xAI:
- OpenAI Chat Completions received its tool schemas under
input_schema, the Anthropic key. The model never saw the parameters of a tool and called it with no arguments. Measured after the fix, all eight reachable provider routes call a flat two-parameter tool with both arguments filled in. - A flat argument object became null on the xAI route, so the tool ran with no arguments. The built-in
MEMORYtool has flat parameters and was affected. - Tool-call arguments above 32767 bytes raised
ORA-06502. - The text the assistant writes next to a tool call was dropped from the returned history, which the agent layer reloads as its conversation.
- A null
usage, a nullcompletion_tokens_detailsand a missingchoicesarray each raisedORA-30625. A response with no choices now raises a provider error that carries the body.
OpenAI Responses API:
finish_reasonwas read from a key the Responses API has never sent, so every run reportedstop. The valueslength,content_filterandmax_tool_calls_exceededwere unreachable.- A conversation that hit the tool-call budget could not be resumed. The normalized history held Anthropic shapes, which the replay converter does not read. Both sides now go through the
uc_ai_message_apibuilders, which also makes thetool_callevent fire. - A tool result above 32 KB broke replay, because four reads took the 32767-byte string path.
- Reasoning with
store => falsedropped the chain of thought on every tool turn. UC AI now asks forreasoning.encrypted_content. - A system prompt above 32 KB raised
ORA-06502. The value is a CLOB on its whole path now, and a 40176-character prompt arrives complete. - A response with no usable output returned a null
final_message. It raises now.
OCI:
- A plain llama answer carries
"toolCalls": [], and UC AI read the presence of that key as a tool turn. Every plain answer triggered one more request, andmax_tool_callscounted no call, so it did not stop the loop. - The replay converters degraded a history that a caller passes back, which is what an agent continuation does. GENERIC turned a tool call into the prose
Tool call: Xand a tool result into a user message. Cohere sent the arguments as a JSON null,toolCalls.parametersas a JSON-encoded string, and oneCHATBOTmessage per content item. - Parallel tool calls shared one content array, so message n carried the results of calls 1 to n.
- The Cohere finish reasons
ERROR_TOXIC,ERROR,ERROR_LIMITandUSER_CANCELall reported as a clean stop. The message the provider sends with them now arrives aserror_message. parameterDefinitionswas{}for every flat-schema tool on the Cohere format, so the model saw no parameters.- An empty Cohere history raised
ORA-30625.
Ollama:
- Local tool calling did not work on the native
/api/chatroute. A tool-call turn arrives with an empty content, and the content guard fired before the tool calls were read, so the run ended withORA-20304. - A user message that carries only files was dropped, so the image never reached the model. On
gemma4:26bthe same call went from 23 to 79 prompt tokens after the fix. - Any file was appended to
imageswithout a check of its media type, so a PDF was sent as an image. An unsupported media type raises now, as it does on the OpenAI path. - A null message object, a JSON-null
tool_calls, and missing or null arguments each raisedORA-30625or carried over the arguments of the previous loop iteration.
Tools:
get_tools_object_param_nameraisedTOO_MANY_ROWSfor a tool with two top-level parameters, one of them an object.- A tool argument above 32 KB failed in the bind, because
apex_plugin_utilcannot carry a CLOB. An oversized payload takes theDBMS_SQLbranch now. $schemaandadditionalPropertiesare no longer sent to OCI. Agoogle.*model on the OCI route returned HTTP 400 for$schema, and neither keyword tells a model anything.
Structured output:
- The converter removed the descriptions, the titles and the author’s
requiredlist from every schema. A description is an instruction the model obeys, so the answer changed. UC AI now removes a keyword only where the provider rejects it, and writes the removed constraint into the description of the node. - The Google conversion dropped
oneOf,allOf,not,if/then/else,containsandprefixItems. A nested Pydantic model converted to its description alone, andgemini-2.5-flashanswered with a string where the schema meant an object.oneOfbecomesanyOf,allOfis flattened into the parent, and a node that the proto cannot express is refused rather than emitted empty. - The
additionalPropertiesgate did not fire for{"type": ["object", "null"]}, while the required-list logic did, so OpenAI strict mode rejected the schema. - Tuple-form
itemswas ignored, because it was read as an object. A tuple is now rewritten to oneitemsnode that holds ananyOf. No decoder can express per-position typing, and the guide records that limit. - A recursive
$refhad no visited set and counted every descent against the depth limit, so a small self-referencing tree inflated to thousands of empty nodes. A cycle raises now, and the keys next to the$refare merged instead of dropped. constwas dropped, a JSON null enum member became the four-character stringnull, and an enum-only node carried no type.$defs,definitionsand the combinator arrays were not recursed into, so an unclosed object went out and the provider returned HTTP 400.- Anthropic and OpenAI return HTTP 400 for
not,contains,if,propertyNamesand someformatvalues. UC AI strips them against a measured whitelist and folds the removed rule into the description, which the model obeys.
New models:
- Mistral, in
uc_ai_mistral:c_model_mistral_large,c_model_mistral_medium,c_model_mistral_small,c_model_magistral_medium,c_model_magistral_small,c_model_codestral,c_model_devstral_medium,c_model_devstral_small,c_model_ministral_8b,c_model_ministral_3b,c_model_pixtral_large, plus the embedding modelsc_model_mistral_embedandc_model_codestral_embed. See Mistral. - Anthropic, in
uc_ai_anthropic:c_model_claude_5_fable,c_model_claude_5_opus,c_model_claude_5_sonnetandc_model_claude_4_8_opus. - OpenAI, in
uc_ai_openai:c_model_gpt_5_6_sol,c_model_gpt_5_6_terra,c_model_gpt_5_6_luna,c_model_gpt_5_5andc_model_gpt_5_5_pro. - Google, in
uc_ai_google:c_model_gemini_3_7_flash,c_model_gemini_3_6_flash,c_model_gemini_3_5_flashandc_model_gemini_3_5_flash_lite.c_model_gemini_3_1_flash_litenow holds the GA idgemini-3.1-flash-lite, because Google shut the preview id down. - xAI, in
uc_ai_xai:c_model_grok_4_6,c_model_grok_4_5,c_model_grok_4_3andc_model_grok_build_0_1. OCI reaches Grok 4.3 asuc_ai_oci.c_model_grok_4_3. - A retired constant stays in its package, under a comment that says a request with that model fails. The provider pages and the consumer skills name only current models.
To upgrade run upgrade_packages.sql script attached to the GitHub release.
For new installations follow this guide.
Features:
- Register a PL/SQL procedure to receive events during
generate_textcalls - Event types:
assistant_text,assistant_reasoning,tool_call,tool_result,response_complete - Register via
uc_ai.set_event_callback('MY_PKG.ON_AI_EVENT')and clear withuc_ai.clear_event_callback - Useful for streaming-style UIs, audit logging, and observability into long-running AI interactions
- Each event carries a correlation id (
uc_ai.g_request_id) so events from concurrent requests can be distinguished - See the event callbacks guide for details
Multi-Agent Systems:
- Profile Agents and Orchestrator Agents now support multi-turn conversations via follow-up messages
- Continue an existing agent execution with additional user input while preserving full conversation history
Responses API:
- OpenAI’s Responses API is now enabled by default across OpenAI, Ollama, and OCI integrations
- Previously opt-in via
uc_ai_openai.g_use_responses_api; existing code continues to work - Improved Ollama Responses API integration with proper base URL error handling and usage tracking
- OCI + Responses API now tracks token usage
- New
uc_ai_tools_api.merge_tool_from_schemafunction for upsert semantics — creates a tool if it doesn’t exist, or updates its description, parameters, and tags if it does create_tool_from_schemaretains its original create-only behavior (will raise if the tool code already exists)- See the tools guide for an example
New Utilities:
- New
uc_ai_utilspackage returning available models and providers programmatically — useful for building model picker UIs - New
uc_ai_errorpackage for centralized error handling across the framework - Centralized JSON response parsing with consistent error handling across all providers
Other:
- Added trigger to automatically set
started_attimestamp onuc_ai_agent_executionsrows - Package AUTHID changes for consistent invoker/definer rights handling
- Fix: tool call arguments are now correctly wrapped in
json_object_t - Fix: Anthropic combining reasoning with tools (#46)
- Fix: documentation correction —
uc_ai.g_use_responses_apiwas incorrect, the actual setting isuc_ai_openai.g_use_responses_api(#50) - New models: Claude 4.7 Opus and additional constants across Anthropic, Google, OCI, OpenAI, and xAI
To upgrade first run v25.7_to_v26.1.sql followed by upgrade_packages.sql script attached to the GitHub release.
For new installations follow this guide.
Features:
Multi-Agent Systems (v1):
- Build AI systems by composing multiple specialized agents
- Profile Agents: Wrap prompt profiles with execution tracking and session grouping
- Workflows: Sequential, conditional, parallel, and loop-based deterministic pipelines with structured data flow between steps
- Orchestrator: Central AI agent autonomously delegates to specialized agents via tool calling
- Conversations: Multiple agents collaborate in round-robin or AI-moderated dialogue
- Input mapping syntax for data flow between agents (
{$.input.*},{$.steps.<output_key>},{$.chat_history}) - Execution tracking with token usage aggregation via
uc_ai_agent_executionstable - See the multi-agent systems documentation to get started
- Centralized, versioned prompt template management
- Store reusable AI prompt templates (system + user prompts) with
{placeholder}substitution - Version profiles and manage status (draft, active, archived)
- Configure model settings, providers, structured output schemas and tool calling directly in the profile
(OpenAI) Responses API:
- OpenAI announced that they are focusing on their Responses API for all new features and models
- New
uc_ai_responses_apipackage for native integration with OpenAI’s Responses API - Currently it’s opt-in by setting
uc_ai_openai.g_use_responses_apito TRUE - In the future Ollama, Openrouter, and other providers who might adopt it will use that package as well
Other:
- Anthropic structured output support
- New global variable
uc_ai.g_max_tool_callsto control the maximum number of tool calls allowed in a single AI request - Improved token usage reporting across all providers
- Bug fixes
- Added constants for new models
To upgrade run upgrade_packages.sql script attached to the GitHub release.
For new installations follow this guide.
Features:
- New providers: xAI and OpenRouer
- Support generate embeddings for all providers
- Support Toon for token efficient representation
- Support providers which have OpenAI compliant APIs
- Overrride base URL for all provider via
uc_ai.g_base_url. This makes it possible to use hosted models on other URLs (Amazon Bedrock, Azure, etc.) as long as they have the same API structure. - Global APEX web credential variable
uc_ai.g_apex_web_credentialwhich will be used as fallback when no provider specific one is provided - Global Reasoning settings variable
uc_ai.g_reasoning_level(low, medium, high) which will be mapped to provider specific implementations - Global UC AI version constants
uc_ai.c_versionanduc_ai.c_version_num - Bug fixes
- Added constants for new models
To upgrade run upgrade_packages.sql script attached to the GitHub release.
For new installations follow this guide.
Features:
- Initial support to generate embeddings (currently just Ollama)
- Support providers which have OpenAI compliant APIs
- Instead of using the API key function you can now use APEX Web Credentials
- Logger is now optional. If you have it installed UC AI will use it. Otherwise it will use
apex_debug. You can useuc_ai_logger.enable_apex_debugwhen you are calling UC AI from no APEX contexts and need logging. - Uninstall script
- Compatibility fixes
- Bug fixes
- Added constants for new models
To upgrade first run v25.4_to_v25.5.sql followed by upgrade_packages.sql script attached to the GitHub release.
For new installations follow this guide.
Features:
New Provider: Oracle OCI
- Use Cohere, Llama, xAI, and Gemini via the Oracle Cloud
- See the OCI provider documentation to get started
Structured Output
- Force AIs to respond in a specific JSON format to make it easy to extract information
- See the structured output documentation to get started
Tools:
- Supported way to create tools is now
uc_ai_tools_api.create_tool_from_schema. See the registering tools docs section for details. Existing tool definitions should not be impacted. - Added global
uc_ai.g_tool_tagsparameter to filter which tools are available for an AI request.See the using tools docs section for details
Other:
- JSON Schema Builder to help structured output and tool definitions
- Documentation improvements
- Added new models
To upgrade just run the upgrade_packages.sql script attached to the GitHub release.
Breaking Changes:
You need to manually enable tools usage any time you want to use tools in the AI response:
-- ...begin uc_ai.g_enable_tools := true; -- enable tools usage
l_result := uc_ai.generate_text( p_user_prompt => 'What is the email address of Jim?', p_system_prompt => 'You are an assistant to a time tracking system. Your tools give you access to user, project and timetracking information. Answer concise and short.', p_provider => uc_ai.c_provider_google, p_model => uc_ai_google.c_model_gemini_2_5_flash );
-- ...Before UC AI sent all tools that were available for the model to the AI provider. This could lead to unexpected behavior or reduced accuracy if the AI tried to use a tool that was not relevant to the conversation.
New Features:
-
Reasoning: Let the LLMs think step by step before answering. This is useful for complex questions where the AI needs to reason about the answer. Read more about it in the documentation.
-
New Provider: Ollama: UC AI now supports the Ollama provider, which allows you to run open models locally on your machine. This is useful for scenarios where you want to run LLMs without relying on an external API. Read more about it in the documentation.
-
Improved documentation: The documentation has been improved to provide more examples and explanations of how to use UC AI. For example there is now a separate page for each provider with in-depth information and best practices.
To upgrade just run the upgrade_packages.sql script attached to the GitHub release.
Attach files to conversations and let AIs analyze them
Attach files to your AI conversations and let the AI analyze them. This is useful for scenarios where you want the AI to answer questions based on the content of a file, such as a PDF or an image.
Watch this YouTube video to see how it works, or read the documentation for detail.
declare l_messages json_array_t := json_array_t(); l_content json_array_t := json_array_t(); l_result json_object_t; l_final_message clob;begin -- Create a system message to set the context for the AI l_messages.append(uc_ai_message_api.create_system_message( 'You are an assistant answering trivia questions about TV Shows. Please answeer in super short sentences.'));
-- The user message consists of two parts: the file content and a text content
-- Add the file content to the user message l_content.append(uc_ai_message_api.create_file_content( p_media_type => 'application/pdf', p_data_blob => (select blob_content from your_table where id = 1), p_filename => 'change_me.pdf' ));
-- Add text content to the user message l_content.append(uc_ai_message_api.create_text_content( 'What is the TV show called of the characters that are inside the attached PDF?' )); -- Add the user message with the file content and text content to the messages array l_messages.append(uc_ai_message_api.create_user_message(l_content));
-- Call the AI service to generate a response based on the messages l_result := uc_ai_google.generate_text( p_messages => l_messages, p_model => uc_ai_google.c_model_gemini_2_5_flash, p_max_tool_calls => 3 );
l_final_message := l_result.get_clob('final_message'); sys.dbms_output.put_line('Last message: ' || l_final_message); -- > The AI should respond with "The Office" or similarend;Continue conversations with different providers and models
We added a second signature to the generate_text function that allows you to pass an array of messages instead of a single user prompt and system prompt. This enables you to continue conversations.
Because of the standardized message format, you can switch between different AI providers and models in the middle of a conversation, allowing for flexibility in AI interactions:
declare l_messages json_array_t; l_result json_object_t; l_response_messages json_array_t;begin -- Initial conversation l_result := uc_ai.generate_text( p_user_prompt => 'What is the rarest chemical element?', p_system_prompt => 'You are an assistant for chemical students in school.', p_provider => uc_ai.c_provider_openai, p_model => uc_ai_openai.c_model_gpt_4o_mini );
dbms_output.put_line('Response: ' || l_result.get_string('final_message'));
-- Get the complete message history from the first call l_messages := l_result.get_array('messages');
-- Add a follow-up question l_messages.append( uc_ai_message_api.create_simple_user_message( 'How is it called in german, japanese and portuguese?' ) );
-- Continue the conversation with full context l_result := uc_ai.generate_text( p_messages => l_messages, p_provider => uc_ai.c_provider_google, p_model => uc_ai_google.c_model_gemini_2_5_flash );
dbms_output.put_line('Follow-up response: ' || l_result.get_string('final_message'));end;Documented the message array signature
You can find the message array type definition here.
New tests make sure that the responses comply with the format.
Initial release of UC AI.