Skip to content

feat(tools): use_oci + describe_oci — open-spec OCI control plane for agents#254

Merged
fede-kamel merged 6 commits into
mainfrom
feat/use-oci-tools
May 23, 2026
Merged

feat(tools): use_oci + describe_oci — open-spec OCI control plane for agents#254
fede-kamel merged 6 commits into
mainfrom
feat/use-oci-tools

Conversation

@fede-kamel
Copy link
Copy Markdown
Contributor

@fede-kamel fede-kamel commented May 22, 2026

Closes #253.

Summary

Two built-in tools that, together, expose every operation in the OCI Python SDK to an agent through a single open-spec primitive — no per-service plumbing.

  • describe_oci(service?, client?, operation?) — runtime introspection. Progressively zooms into the SDK:

    • no args → list every service module under oci.* (~190 of them)
    • service → list every *Client class in that module
    • service + client → list operations, partitioned read-only vs mutating
    • service + client + operation → parameter schema parsed from the OCI SDK :param: docstring + signature
  • use_oci(service, client, operation, parameters, ...) — execute any operation. Builds the right oci.<service>.<Client> with the requested auth (api_key, security_token, instance_principal, resource_principal), invokes the method, and serialises the response (snake_case Python attrs → camelCase wire names via attribute_map). Result includes http_status + opc-request-id for OCI-side traceability.

The agent loop becomes:

from locus.agent import Agent
from locus.models import get_model
from locus.tools import describe_oci, use_oci

agent = Agent(
    model=get_model("oci:openai.gpt-4o", profile="MY_GENAI", region="us-chicago-1"),
    tools=[describe_oci, use_oci],
    system_prompt="You can call OCI. Discover with describe_oci first, then use_oci.",
)
agent.run_sync("Are there any compute instances running anywhere in my tenancy?")

The model autonomously:

  1. Calls describe_oci(service=\"core\", client=\"ComputeClient\", operation=\"list_instances\") to learn the parameter shape.
  2. Calls use_oci(...) three times — once per subscribed region.
  3. Returns an English summary citing the real instance name + OCID it found.

Adding a new OCI resource type to an agent no longer requires writing or registering any new code — one execution path, one auth code-path, one serialiser, one mutation gate for all of OCI.

Safety

use_oci is read-only by default. Operation names not starting with list_/get_/head_/summarize_/describe_/search_/fetch_/compute_/preview_/validate_/test_ are refused unless either allow_mutations=True is passed explicitly or LOCUS_USE_OCI_ALLOW_MUTATIONS=1 is set. Enforced in code, not via interactive prompt (locus tools must be non-interactive).

What's in the PR

  • src/locus/tools/oci.py — both tools, the docstring parser, the OCI-model serialiser, and the auth router
  • src/locus/tools/builtins.py + src/locus/tools/__init__.py — re-exports
  • examples/notebook_70_oci_tools.py — three-part walkthrough: introspection, direct dispatch, agent-driven NL

Test plan

  • uv run pytest tests/unit/test_tools.py tests/unit/test_tools_schema.py tests/unit/test_idempotent_tools.py — 71/71 pass.
  • Pre-commit (ruff lint + format, mypy, codespell, secret scan) — clean.
  • Live smoke against a real tenancy, read-only:
    • identity.IdentityClient.list_compartmentshttp_status=200, returned the tenancy's compartments with real opc-request-id.
    • object_storage.ObjectStorageClient.get_namespace (cross-region against us-chicago-1) → http_status=200, returned the real namespace.
    • core.ComputeClient.list_instances across three subscribed regions → returned the running instance with its real OCID.
  • Safety gate: delete_bucket without allow_mutations → refused with explanatory error.
  • describe_oci() returns 189 service modules; the docstring parser correctly extracts compartment_id (required), compartment_id_in_subtree (optional bool), access_level (optional str), etc. from list_compartments.

Narrative writeup

For a friendlier explanation of why this primitive matters — the design tradeoffs, the discover-then-execute pattern, a live trace against a real tenancy — see this gist:

https://gist.github.com/fede-kamel/d697e19290e94ecb00791e6dc1ff7d30

@oracle-contributor-agreement oracle-contributor-agreement Bot added the OCA Verified All contributors have signed the Oracle Contributor Agreement. label May 22, 2026
…lane coverage

Two built-ins that, together, expose every operation across the ~190
OCI Python SDK service modules to an agent without per-service plumbing:

- describe_oci(service?, client?, operation?) — runtime introspection.
  Progressively zooms: list services, list clients in a service, list
  operations on a client (partitioned read-only vs mutating), or
  return the parameter schema for one operation (parsed from the OCI
  SDK :param: docstring + signature).

- use_oci(service, client, operation, parameters, ...) — execute any
  operation. Builds the right oci.<service>.<Client> with the
  requested auth (api_key, security_token, instance_principal,
  resource_principal), invokes the method, and serialises the
  response (snake_case Python attrs -> camelCase wire names via
  attribute_map). Includes http_status + opc-request-id in the result
  for OCI-side traceability.

Read-only by default. Operations whose names don't start with a
read-only prefix (list_/get_/head_/summarize_/describe_/search_/
fetch_/compute_/preview_/validate_/test_) are refused unless either
allow_mutations=True is passed or LOCUS_USE_OCI_ALLOW_MUTATIONS=1 is
set. Enforced in code, not via an interactive prompt.

The agent loop becomes: NL prompt -> describe_oci (verify shape) ->
use_oci (execute) -> English summary. One execution path, one auth
code-path, one serialiser, one mutation gate for all of OCI.

Closes #253.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
- concepts/tools.md: new "Open-spec built-ins for the Oracle estate"
  section (placed after "Practical recipes", before "Common gotchas")
  that frames use_oci + describe_oci as the agentic-framework angle
  on cloud control planes — discover + execute, read-only by default
  — and cross-links to the Oracle 26ai RAG primitives (Notebook 06)
  for the data-plane half of the picture.
- FEATURES.md: row in the existing Tools table for the new built-ins.
  Slotted next to the other tool features rather than the top banner.
- mkdocs.yml: register notebook 70 under "Server & full pipelines".
- examples/notebook_70_oci_tools.py: capitalise the docstring title
  so the auto-generated H1 reads cleanly.
- docs/notebooks/notebook_70_oci_tools.md: rendered notebook page.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
This is an agentic framework, not a database product. The notebook
nav read database-first because the Oracle 26ai cluster sat at
slot 6-13 — right after "start here". Move that group below the
Server & full pipelines block (and rename it "Oracle Database 26ai
(optional persistence layer)") so Agent Foundations is what readers
hit immediately after the OCI GenAI quickstart. Slot numbers stay
6-13 to avoid churn in the rendered notebook files.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
This is an agentic framework — the notebook nav now reflects that.
Renumber every notebook 6-54 so that the top-to-bottom order is
strictly sequential AND topically grouped:

  1-5    OCI Generative AI (unchanged)
  6-15   Agent Foundations  (was 14-21; now includes Oracle agent
         memory at 9 and Oracle store at 10, alongside Conversation
         memory at 8)
  16-23  Graphs & composition (was 22-29)
  24-34  Multi-agent (was 30-40)
  35-37  Reasoning & structured output (was 41-43)
  38-44  RAG (was 44-46; now includes Oracle 26ai RAG at 41,
         ADB document loader at 42, in-DB chunker at 43, in-DB
         embeddings at 44)
  45-49  Skills, playbooks & plugins (was 47-51)
  50-57  Production (was 52-57; now includes Oracle 26ai
         checkpointer at 53 and Oracle versioned checkpoint saver
         at 54 alongside Checkpoint backends at 52)
  58-62  Cognitive router & observability (unchanged)
  63-67  Real-world workflows (unchanged)
  68-70  Server & full pipelines (unchanged)

The Oracle 26ai notebooks now live in the agent sections where they
belong — RAG, Agent Foundations, Production — rather than in a
dedicated "database" section. Locus is an agentic framework; Oracle
26ai is the persistence layer when you want it.

Also:
- docs/workbench.md: lead the "Start with Oracle" demos with the
  natural-language OCI driver (notebook 70 — use_oci + describe_oci)
  before Oracle 26ai RAG, since it's the broadest agentic primitive.
- docs/concepts/tools.md: fix the cross-link from "Notebook 06" to
  "Notebook 41" now that Oracle 26ai RAG sits in the RAG section.

Mechanically: 96 renames (.py + .md) executed in two phases through
a TMP namespace to avoid collisions, plus 376 cross-reference
rewrites across mkdocs.yml, README, CHANGELOG, every concept page,
every rendered notebook, and the workbench docs.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
CI on the PR flagged two issues on Python 3.14:

- `cast("type", client_cls)` triggered `[redundant-cast]` on mypy 3.14
  (the isinstance narrowing one line above is enough). On mypy 3.12
  the cast is still required to satisfy `[no-any-return]`. Suppress
  both codes inline so the same source compiles cleanly under both
  versions.

- `src/locus/tools/oci.py` had 14% coverage — under the per-file 90%
  ratchet floor for new files. Add tests/unit/test_tools_oci.py
  covering the pure-Python logic (docstring parser, mutation gate,
  read-only classifier, OCI-model serialiser, auth-router branches,
  client/operation resolution errors, plus a full success path
  mocked through `_build_config_and_signer` and `_resolve_client_class`).
  Coverage rises from 14% to 92%.

All 70 new tests pass; pre-commit (ruff, mypy 3.12, codespell)
clean.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
@fede-kamel fede-kamel force-pushed the feat/use-oci-tools branch from a193377 to 0d15447 Compare May 23, 2026 02:24
…erence

The arXiv citation `2604.23366` was inlined as a bare code span / plain
link on five surfaces (concept page, capabilities page, FEATURES page,
the rendered notebook page, and the source notebook docstring). The
README already carries the full author line and a BibTeX block, so the
canonical attribution exists — these five surfaces just didn't echo
it.

Update them all to render the citation as
"Federico A. Kamelhar (2026), arXiv:2604.23366" with the arxiv.org/abs
URL, so the author shows on every page that mentions the paper.

No source code changes; mkdocs build --strict clean.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
@fede-kamel fede-kamel merged commit 04a9b72 into main May 23, 2026
10 checks passed
fede-kamel added a commit that referenced this pull request May 23, 2026
#255)

Bumps version 0.2.0b19 → 0.2.0b20 and rolls the [Unreleased] section
into a dated [0.2.0b20] entry.

Bundles:

- PR #254 — feat(tools): use_oci + describe_oci open-spec OCI
  primitive (~190 services covered by one execution path,
  read-only by default), notebook 70 walkthrough, docs reorg.
- PR #249 — feat(graph): LangGraph parity shims (sync invoke,
  mermaid/ascii renderers).
- PR #247/#251 — fix(a2a): A2AClient httpx timeout configurable.
- PR #245 — fix(telemetry): OTel trace context propagation through
  the agent run loop.
- PR #246 — docs(readme): rebrand around the locus stack.
- PR #248 — docs(site): canonical URL locusagents.oracle.com.

Signed-off-by: Federico Kamelhar <federico.kamelhar@oracle.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

OCA Verified All contributors have signed the Oracle Contributor Agreement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Open-spec OCI tools: use_oci + describe_oci for whole-control-plane agent coverage

1 participant