Skip to content

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.sql sets total_input_tokens and total_output_tokens to 0 on every existing workflow, handoff, and conversation row in uc_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_code attribution. 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_agent cannot see an agent row that the calling transaction has not committed. Earlier, this combination produced a cryptic ORA-00060 self-deadlock. It now raises a clear error that names the cause. Add a COMMIT after create_agent (and after change_status) before the first execute_agent call.

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_index stays empty and turn_count on 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 explicit p_session_id or p_parent_exec_id still wins.
  • iteration_count and tool_calls_count hold their real values. Both columns of uc_ai_agent_executions were 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_reason has the new value unknown. A provider reason that UC AI does not map came through raw before, and matched none of the documented values. It is now unknown, and the new provider_finish_reason holds 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 sends reasoning_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 read city directly and no longer through parameters. A tool that declares its parameters flat, which is the normal shape, is not affected.
  • DEDICATED serving mode on OCI needs the OCID of the endpoint. Set uc_ai_oci.g_endpoint_id. Without it the mode raises ORA-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_scheduler job 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: download uc-ai-chat-26.3.zip from 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 in uc_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 the g_extra_body config 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 request tools array. Executed by the provider, not locally; sent even when local tools are disabled. Also settable via the g_provider_tools config 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 a PURE MLE execution context inside an isolated, low-privilege sandbox schema (installed once via scripts/install_ptc_sandbox.sql from a repository checkout, or install_ptc_sandbox_complete.sql attached 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 the before_tool_call hook. Per-tool p_code_mode_access (direct | code | both) controls whether a tool is a normal tool, code-only, or both. create_tool_from_schema defaults it to both. merge_tool_from_schema defaults 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 the g_extra_headers config 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_text and uc_ai.generate_embeddings therefore 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 that p_config omits falls back to the framework default and not to the current global value.

Results:

  • Three new result properties of generate_text: block_reason holds the Google reason for a blocked prompt, provider_finish_reason holds the word the provider sent when UC AI maps none of its own, and error_message holds 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_agent and generate_text take a new p_run_context parameter, 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_context and uc_ai_agent_sessions.run_context record the effective context.
  • Config-driven calls keep their execution context: uc_ai_settings.build_from_config now snapshots the execution context, as build_from_globals does. A generate_text call with p_config inside 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”; pass p_created_by to restrict the update to the user who opened the session. list_sessions now returns title, 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_rating up/down, plus an optional feedback_comment and feedback_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_sessions now 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 pass APP_USER against 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, pass null.

  • 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, maintained turn_count and message_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 with uc_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, in seq order, 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 carries agent_code, the agent that produced the message — the sub-agent for wrapper turns, the turn’s own agent otherwise. Read it with uc_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 MEMORY tool 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_agent writes 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, or context. With the scope context an agent keeps one store per run-context value, for example one memory per document. Set p_scope => 'context' and p_context_key => 'document_id'. Name a p_store_code to let several agents share one store per value.
  • Caps and housekeeping: max_file_chars, max_files, and the optional max_store_chars bound the growth of a store. uc_ai_memory.expire_files deletes the files that nobody used for a number of days. The view uc_ai_v_memory_files shows what a store holds.

Multi-Agent Systems:

  • Handoff agents (c_type_handoff) are now fully implemented with tool-based transfers: the engine registers a temporary transfer_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 at max_handoffs runs without transfer tools and must answer. Results include final_agent_code, handoff_count, handoff_trail, and max_handoffs_reached; transfer tool calls are persisted in the session message log. Handoff orchestration configs are now validated at create_agent time (targets must be existing active profile agents).

  • Multi-level handoff routing: handoff_agents entries accept an optional can_transfer_to array 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_state after 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_details includes 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 via APEX_JSON for 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 -20455 instead of silently returning NULL; 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 :parameters and stores its return value under output_key with its real JSON type. A returned {"__control__":"stop"} halts the remaining steps.

  • File input for agents: execute_agent accepts p_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_executions now records who started a run and from where — created_by, db_user, the APEX user, session, application and page, plus os_user, host, ip_address, module, action, client_identifier, sid, and an env_context JSON 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 — public for an anonymous APEX visitor, authenticated for a logged-in APEX user, and db for 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 no audience value.

  • Recursion guard: agent delegation now has a maximum nesting depth. A circular reference — an agent that delegates back to itself — raises ORA-20405 with 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_agent deletes 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_referenced sees 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_tool is the whole handler of a tool that runs another agent. Register the tool with p_function_call => 'return uc_ai_agents_api.run_agent_as_tool(''my_agent'', :parameters);' and every agent, workflow step, and generate_text call 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 is run_agent_as_tool and whose parameters are the input_schema of the agent. The signature is register_agent_as_tool(p_agent_code, p_tool_tag, p_tool_code). It lost p_exec_id and p_session_id in this release, because the session and the parent execution now come from the context of the run. The new p_tool_code names 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 raises ORA-00001. For a setup script that runs more than once, delete the tool first, or register the tool with uc_ai_tools_api.merge_tool_from_schema and the one-line handler above.

Prompt profiles:

  • Update a profile from its row: uc_ai_prompt_profiles_api.update_prompt_profile now takes a uc_ai_prompt_profiles%rowtype. Read the row with get_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_profile overloads 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 with pragma deprecate.

Extension points:

  • Execution hooks around an agent run: an optional hook package can implement before_execution and after_execution. before_execution runs before any execution row or token spend and can raise to veto the run (fail-closed). after_execution is best-effort — UC AI logs an error there and continues.
  • before_tool_call hook: 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_prompt hook: 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 hardcoded maxTokens of 600 and always reported finish_reason as stop, so a truncated answer looked complete. Truncation is now reported. Also settable with the g_max_tokens OCI 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), and uc_ai_mistral.c_model_ministral_3_14b and uc_ai_mistral.c_model_zai_glm_5_2 on the Mistral platform. uc_ai_utils.get_models returns all five.
  • Fix: c_model_gemini_2_0_flash_lite held gemini-2.0-flash_lite. The underscore is not part of the model id, so the constant named a model that does not exist. It is gemini-2.0-flash-lite now.
  • OCI: reach a dedicated AI cluster (uc_ai_oci.g_endpoint_id). DEDICATED serving mode now sends the OCID of the endpoint and no model id, on the chat path and on the embeddings path. ON_DEMAND is unchanged and stays the default. Also settable with the g_endpoint_id OCI config key and on a prompt profile.

Internal:

  • New uc_ai_settings package 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_text snapshots 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_executions also requires completed_at for a terminal status, and gains an updated_at audit 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.md format, 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 project
    npx skills add United-Codes/uc_ai
    # or one, for one agent
    npx skills add United-Codes/uc_ai --skill uc-ai-tools -a claude-code

    You 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_tools and uc_ai.g_tool_tags set 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 condition in an object form ({"type": ..., "expression": ...}) was read with get_string and therefore ignored, so the step always ran. create_agent now rejects anything that is not a string with ORA-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 in summary[].text and not in a top-level field.
  • Duplicate or case-variant tool tags raised ORA-00001 on create_tool_from_schema and merge_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_OBJECTS for the runner package body, which an EXECUTE grant never exposes.
  • Prompt profiles and agents could not enable code mode: g_enable_programmatic_tools had no branch in the configuration reader, so the documented declarative option failed with ORA-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_tokens with ORA-20503.
  • A loop workflow whose exit_condition read a later step’s output raised ORA-20451. The unresolved path collapsed into a malformed expression.
  • OCI GENERIC assistant messages that carry both content and toolCalls lost the tool calls. An empty content array short-circuited tool-call handling, which produced an empty final message and a tool call count of 0.
  • final_message is 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"}, and claude-opus-5, claude-opus-4-8, claude-opus-4-7, claude-sonnet-5 and claude-fable-5 reject 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_tokens as null, which returned Input should be a valid integer.
  • A tool_use whose input was JSON null raised ORA-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 reports content_filter with the reason in block_reason.
  • Gemini 3 takes thinkingLevel and not thinkingBudget, 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, medium and high raised ORA-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 MEMORY tool 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 null completion_tokens_details and a missing choices array each raised ORA-30625. A response with no choices now raises a provider error that carries the body.

OpenAI Responses API:

  • finish_reason was read from a key the Responses API has never sent, so every run reported stop. The values length, content_filter and max_tool_calls_exceeded were 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_api builders, which also makes the tool_call event fire.
  • A tool result above 32 KB broke replay, because four reads took the 32767-byte string path.
  • Reasoning with store => false dropped the chain of thought on every tool turn. UC AI now asks for reasoning.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, and max_tool_calls counted 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: X and a tool result into a user message. Cohere sent the arguments as a JSON null, toolCalls.parameters as a JSON-encoded string, and one CHATBOT message 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_LIMIT and USER_CANCEL all reported as a clean stop. The message the provider sends with them now arrives as error_message.
  • parameterDefinitions was {} 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/chat route. A tool-call turn arrives with an empty content, and the content guard fired before the tool calls were read, so the run ended with ORA-20304.
  • A user message that carries only files was dropped, so the image never reached the model. On gemma4:26b the same call went from 23 to 79 prompt tokens after the fix.
  • Any file was appended to images without 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 raised ORA-30625 or carried over the arguments of the previous loop iteration.

Tools:

  • get_tools_object_param_name raised TOO_MANY_ROWS for 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_util cannot carry a CLOB. An oversized payload takes the DBMS_SQL branch now.
  • $schema and additionalProperties are no longer sent to OCI. A google.* 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 required list 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, contains and prefixItems. A nested Pydantic model converted to its description alone, and gemini-2.5-flash answered with a string where the schema meant an object. oneOf becomes anyOf, allOf is flattened into the parent, and a node that the proto cannot express is refused rather than emitted empty.
  • The additionalProperties gate did not fire for {"type": ["object", "null"]}, while the required-list logic did, so OpenAI strict mode rejected the schema.
  • Tuple-form items was ignored, because it was read as an object. A tuple is now rewritten to one items node that holds an anyOf. No decoder can express per-position typing, and the guide records that limit.
  • A recursive $ref had 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 $ref are merged instead of dropped.
  • const was dropped, a JSON null enum member became the four-character string null, and an enum-only node carried no type.
  • $defs, definitions and 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, propertyNames and some format values. 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 models c_model_mistral_embed and c_model_codestral_embed. See Mistral.
  • Anthropic, in uc_ai_anthropic: c_model_claude_5_fable, c_model_claude_5_opus, c_model_claude_5_sonnet and c_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_5 and c_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_flash and c_model_gemini_3_5_flash_lite. c_model_gemini_3_1_flash_lite now holds the GA id gemini-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_3 and c_model_grok_build_0_1. OCI reaches Grok 4.3 as uc_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:

Event Callback System:

  • Register a PL/SQL procedure to receive events during generate_text calls
  • 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 with uc_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

Tools API:

  • New uc_ai_tools_api.merge_tool_from_schema function for upsert semantics — creates a tool if it doesn’t exist, or updates its description, parameters, and tags if it does
  • create_tool_from_schema retains 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_utils package returning available models and providers programmatically — useful for building model picker UIs
  • New uc_ai_error package 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_at timestamp on uc_ai_agent_executions rows
  • 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_api was incorrect, the actual setting is uc_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_executions table
  • See the multi-agent systems documentation to get started

Prompt Profiles:

  • 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_api package for native integration with OpenAI’s Responses API
  • Currently it’s opt-in by setting uc_ai_openai.g_use_responses_api to 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_calls to 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_credential which 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_version and uc_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 use uc_ai_logger.enable_apex_debug when 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

Structured Output

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_tags parameter 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 similar
end;

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.