Add parameterized AI tool instances - #112
Conversation
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>
| /// <summary> | ||
| /// Gets or sets an optional category used to group sources in the management UI. | ||
| /// </summary> | ||
| public string Category { get; set; } |
| /// <param name="context">The building context.</param> | ||
| public Task BuildingAsync(AICompletionContextBuildingContext context) | ||
| { | ||
| if (context.Resource is AIProfile profile && |
There was a problem hiding this comment.
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>()); | ||
|
|
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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; } |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
This is not needed in the docs. Docs should stay focused and not place to answer every question related to code
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>
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.