Skip to content

Add parameterized AI tool instances - #112

Merged
MikeAlhayek merged 8 commits into
mainfrom
ma/add-function-defs
Jul 27, 2026
Merged

Add parameterized AI tool instances#112
MikeAlhayek merged 8 commits into
mainfrom
ma/add-function-defs

Conversation

@MikeAlhayek

Copy link
Copy Markdown
Member

Developers define an IAIToolInstanceDefinition in code and register it with AddAIToolInstanceDefinition(). End users create multiple configured instances of a definition, each supplying its own settings (endpoint, authentication, headers) and a model-facing description up front, then attach them to AI profiles. The model still decides when to invoke, but uses the user's predefined settings; ToolInstanceRegistryProvider surfaces each instance as a distinctly named AITool so multiple instances of the same definition appear as separate purpose-labeled functions to every client (OpenAI, Azure OpenAI) via Microsoft.Extensions.AI.

  • Add Tooling abstractions (AIToolInstance, IAIToolInstanceDefinition, naming, options/builder/entry, tool context, profile metadata) and AICompletionContext.ToolInstanceIds
  • Add AddCoreAIToolInstances()/AddAIToolInstanceDefinition(), catalog and completion-context handlers, and the tool registry provider
  • Add built-in HTTP API Request tool (AddApiRequestToolInstance()) with data-protected credentials
  • Persist AIToolInstance via YesSql (with index) and EntityCore
  • Add management UI and AI profile attachment in MVC.Web and Blazor.Web samples
  • Add unit tests and documentation (new page, sidebar, changelog)

MikeAlhayek and others added 3 commits July 26, 2026 01:59
Developers define an IAIToolInstanceDefinition in code and register it with
AddAIToolInstanceDefinition<T>(). End users create multiple configured
instances of a definition, each supplying its own settings (endpoint,
authentication, headers) and a model-facing description up front, then attach
them to AI profiles. The model still decides when to invoke, but uses the
user's predefined settings; ToolInstanceRegistryProvider surfaces each instance
as a distinctly named AITool so multiple instances of the same definition
appear as separate purpose-labeled functions to every client (OpenAI, Azure
OpenAI) via Microsoft.Extensions.AI.

- Add Tooling abstractions (AIToolInstance, IAIToolInstanceDefinition, naming,
  options/builder/entry, tool context, profile metadata) and
  AICompletionContext.ToolInstanceIds
- Add AddCoreAIToolInstances()/AddAIToolInstanceDefinition<T>(), catalog and
  completion-context handlers, and the tool registry provider
- Add built-in HTTP API Request tool (AddApiRequestToolInstance()) with
  data-protected credentials
- Persist AIToolInstance via YesSql (with index) and EntityCore
- Add management UI and AI profile attachment in MVC.Web and Blazor.Web samples
- Add unit tests and documentation (new page, sidebar, changelog)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Redesign the parameterized tool feature around a source-aware catalog idiom
so the API reads as "author a blueprint in code, users create configured
entries via UI":

- Replace the IAIToolInstanceDefinition interface with a public abstract
  AIToolSource class that carries its own display metadata (Name,
  DisplayName, Description, Category) and CreateTool, collapsing the former
  Entry/Options/Builder trio.
- Rename AIToolInstance to a sealed public AIToolDefinition : SourceCatalogEntry
  whose Source property records the owning AIToolSource; the management UI
  adapts to that source.
- Rename the registry provider, catalog handler, completion-context handler,
  naming helper, profile metadata, store index, and completion-context IDs to
  the new terminology.
- Register sources with AddAIToolSource<TSource>() (no name/builder); the
  built-in HTTP source registers via AddApiRequestToolSource().
- Update MVC and Blazor sample hosts (routes, labels, view models) and the
  docs page (tool-definitions), sidebar, and changelog.
- Rewrite the tests against the new AIToolSource/AIToolDefinition API.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Renames the parameterized-tool feature from "tool definitions" to "tool
instances" and reshapes the extension model so developers register a source
blueprint (IAIToolInstanceSource) under a unique name via
AddAIToolInstanceSource<TSource>, while users create multiple configured
AIToolInstance entries from the UI.

- AIToolInstance is a sealed SourceCatalogEntry that is name-aware; its unique
  Name drives a distinct, provider-safe function name and its Description lets
  the model tell instances of the same source apart.
- IAIToolInstanceSource replaces the AIToolSource abstract class; sources are
  registered as keyed services resolved by the stored Source, with display
  metadata recorded in AIOptions.ToolInstanceSources.
- ToolInstanceRegistryProvider is a pluggable IToolRegistryProvider; projects
  can register their own provider to add logic such as permission checks.
- Persists AIToolInstance on both YesSql (AIToolInstanceIndex) and EntityCore.
- MVC and Blazor sample hosts register the built-in http-api-request source
  and include full management UI plus AI profile attachment.
- Rewrites unit tests and docs (tool-instances.md) for the new model.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread src/Abstractions/CrestApps.Core.AI.Abstractions/Tooling/AIToolInstance.cs Outdated
Comment thread src/Abstractions/CrestApps.Core.AI.Abstractions/Models/AICompletionContext.cs Outdated
Comment thread src/CrestApps.Core.Docs/docs/core/tool-instances.md
Comment thread src/CrestApps.Core.Docs/docs/core/tool-instances.md Outdated
Comment thread src/CrestApps.Core.Docs/docs/core/tool-instances.md Outdated
Comment thread src/Primitives/CrestApps.Core.AI/AIToolInstanceSourceEntry.cs Outdated
/// <summary>
/// Gets or sets an optional category used to group sources in the management UI.
/// </summary>
public string Category { get; set; }

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Localize

/// <param name="context">The building context.</param>
public Task BuildingAsync(AICompletionContextBuildingContext context)
{
if (context.Resource is AIProfile profile &&

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about ChatInteration?

Will this also work when using ChatClient instance without the ChatCompletion service?

These functions will need to be available for all orchestras not only the default one. Like copilot or Claude orchestrators

services.TryAddEnumerable(ServiceDescriptor.Scoped<ICatalogEntryHandler<AIProfile>, AIProfileHandler>());
services.TryAddEnumerable(ServiceDescriptor.Scoped<ICatalogEntryHandler<AIDeployment>, AIDeploymentCatalogHandler>());
services.TryAddEnumerable(ServiceDescriptor.Scoped<ICatalogEntryHandler<AIProviderConnection>, AIProviderConnectionCatalogHandler>());

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be part of the core service. It should be added by the user when needed and should be also available via builder

/// <summary>
/// Enumerates the authentication strategies supported by the HTTP API request tool.
/// </summary>
public enum HttpApiRequestAuthenticationType

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about oAuth and oAuth 2.0? We should support the common authentication method like password flow

…registry

Refine the parameterized AI tool instances feature:

- Reference instances by their stable unique Name instead of a generated id.
  Rename AIProfileToolInstanceMetadata to the feature-agnostic
  AIToolInstanceMetadata (usable by AI profiles and chat interactions) with an
  InstanceNames property, rename AICompletionContext.ToolInstanceIds to
  ToolInstanceNames, and resolve instances through
  INamedCatalog<AIToolInstance>.FindByNameAsync. Register the instance catalog
  as a named source-document catalog on YesSql and EntityCore, and mark
  AIToolInstanceIndex as an INameAwareIndex.

- Decouple the default registry. AddAIToolInstanceSource<T> gains a
  useDefaultRegistry flag (default true) and a new
  AddDefaultAIToolInstanceRegistry() extension registers the built-in provider,
  so hosts can opt out and supply their own IToolRegistryProvider without ever
  calling RemoveAll<IToolRegistryProvider>().

- Add OAuth 2.0 client-credentials support to the built-in http-api-request
  source. The tool acquires, data-protects, caches, and refreshes access and
  refresh tokens on the AIToolInstance itself (via Put/TryGet), reusing a valid
  token across requests and restarts and only re-authenticating when needed.

Update the MVC and Blazor sample hosts (profile attachment by name, OAuth2
fields on the tool-instance form), extend the tests with OAuth2 acquire/cache/
reuse and refresh coverage, and refresh the docs and changelog.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
/// <summary>
/// Gets or sets the password used for basic authentication. May be data-protected at rest.
/// </summary>
public string BasicPassword { get; set; }

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why prefix Password and Username with Basic. No need

```

:::warning
Do **not** call `services.RemoveAll<IToolRegistryProvider>()` to swap the default provider. `IToolRegistryProvider` is an aggregated abstraction — other framework features register their own providers, and removing all of them strips out those tools too. Use the `useDefaultRegistry: false` opt-out instead, and if you also want the built-in behavior alongside yours, simply leave `useDefaultRegistry` at its default (`true`) and add your provider in addition.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not need to be part of the docs


## How Clients Invoke Instances

Instances are **provider-agnostic** — there is no OpenAI-, Azure OpenAI-, or Azure AI Inference-specific code anywhere in the flow:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not needed in the docs. Docs should stay focused and not place to answer every question related to code

MikeAlhayek and others added 4 commits July 26, 2026 18:47
Framework
- Make AIToolInstance.Clone deep-copy Properties so cached OAuth tokens
  never leak between the stored and in-flight instances.
- Make GetFunctionName collision-resistant by appending a deterministic
  hash suffix when sanitizing or truncating a unique name is lossy.
- Guard the built-in HTTP source against SSRF: model-provided paths can
  no longer redirect a request off the configured base host.
- Enforce tool instance name immutability in the catalog handler.
- Register the tool-instance persistence store on the feature builder
  (AddYesSqlStores/AddEntityCoreStores) instead of the AI services store,
  and rename AddHttpApiRequestTool to AddHttpApiRequestSource and
  AddDefaultAIToolInstanceRegistry to AddDefaultAIToolInstanceRegistryProvider.
- Make ToolInstanceRegistryProvider public with a ShouldIncludeInstanceAsync
  hook so hosts can gate instances (for example by permission) via a subclass.
- Widen the YesSql Source index column and correct terminology.

Samples
- Wire the tool-instance store into both hosts (YesSql for MVC, EntityCore
  for Blazor) and let users attach instances to AI profiles and chat
  interactions.
- Keep the instance Name editable only on create.
- Add the OAuth 2.0 password-grant Username/Password fields to the Blazor
  HTTP form for parity with MVC.

Docs & tests
- Rewrite the tool-instances guide as a consumer guide with accurate names,
  store registration, name immutability, custom registry subclassing, and
  honest notes on sample scope and data-protection requirements.
- Add tests for name collision-resistance, Clone isolation, SSRF host
  pinning, and data-protected token round-trips.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Document the Entity Framework Core store alongside YesSql in the tool
  instances Persistence section with full, parallel examples, and add a
  Quick Start note that the feature works with either provider.
- Rename the "Persisting settings" section to "Persisting metadata" and
  reword the prose to describe stored configuration as instance metadata.
- Remove the provider-agnostic "How Clients Invoke Instances" Q&A block,
  keeping the practical sample-host steps under "Creating Instances in the
  Sample Hosts".
- Remove the RemoveAll<IToolRegistryProvider> :::warning block that was
  flagged as not belonging in the docs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…m under admin AI group

Add a Source dropdown to the AI tool instance Create form in both the
MVC.Web and Blazor.Web sample hosts, populated from the registered
AIOptions.ToolInstanceSources. Selecting a source reveals only that
source's fields; Edit shows the source read-only. Validation now uses
the selected source and gates HTTP-specific checks to it.

Relocate the 'AI Tool Instances' admin menu item from the MCP/A2A
integrations block to the primary admin AI group, right after
'AI Profiles', in both samples.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ode-registered tools

User-configured AI tool instances surfaced to the model under just their
sanitized instance name, while tools registered in code via AddCoreAITool
surface under their bare registered name. A user instance named the same
as a code tool produced two identically named functions in the single
function namespace the model sees, causing duplicate-name rejection or
ambiguous dispatch on OpenAI, Azure OpenAI, and other clients.

AIToolInstance.GetFunctionName() now prepends the public constant
AIToolInstanceExtensions.FunctionNamePrefix ('tool_instance_') to every
instance function name so a user-chosen name can never collide with a
code-registered tool. Instance-vs-instance uniqueness (unique names plus
hash suffix on lossy sanitization) and the 64-char cap are preserved,
with the prefix included in the length budget. Instances are still
resolved internally by their unique instance name, so dispatch is
unaffected.

Update unit tests and document the prefix and rationale.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@MikeAlhayek
MikeAlhayek merged commit cead89d into main Jul 27, 2026
10 checks passed
@MikeAlhayek
MikeAlhayek deleted the ma/add-function-defs branch July 27, 2026 05:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant