@@ -5,6 +5,13 @@ const manifestMocks = vi.hoisted(() => ({
55 listOpenClawPluginManifestMetadata : vi . fn ( ) ,
66 loadPluginManifest : vi . fn ( ) ,
77} ) ) ;
8+ const providerMocks = vi . hoisted ( ( ) => ( {
9+ normalizePluginDiscoveryResult : vi . fn ( ) ,
10+ resolveBundledProviderCompatPluginIds : vi . fn ( ) ,
11+ resolveOwningPluginIdsForProviderRef : vi . fn ( ) ,
12+ resolveRuntimePluginDiscoveryProviders : vi . fn ( ) ,
13+ runProviderStaticCatalog : vi . fn ( ) ,
14+ } ) ) ;
815
916vi . mock ( "../../plugins/manifest-metadata-scan.js" , ( ) => ( {
1017 listOpenClawPluginManifestMetadata : manifestMocks . listOpenClawPluginManifestMetadata ,
@@ -15,7 +22,24 @@ vi.mock("../../plugins/manifest.js", async (importOriginal) => ({
1522 loadPluginManifest : manifestMocks . loadPluginManifest ,
1623} ) ) ;
1724
18- import { resolveBundledStaticCatalogModel } from "./model.static-catalog.js" ;
25+ vi . mock ( "../../plugins/providers.js" , async ( importOriginal ) => ( {
26+ ...( await importOriginal < typeof import ( "../../plugins/providers.js" ) > ( ) ) ,
27+ resolveBundledProviderCompatPluginIds : providerMocks . resolveBundledProviderCompatPluginIds ,
28+ resolveOwningPluginIdsForProviderRef : providerMocks . resolveOwningPluginIdsForProviderRef ,
29+ } ) ) ;
30+
31+ vi . mock ( "../../plugins/provider-discovery.js" , async ( importOriginal ) => ( {
32+ ...( await importOriginal < typeof import ( "../../plugins/provider-discovery.js" ) > ( ) ) ,
33+ normalizePluginDiscoveryResult : providerMocks . normalizePluginDiscoveryResult ,
34+ resolveRuntimePluginDiscoveryProviders : providerMocks . resolveRuntimePluginDiscoveryProviders ,
35+ runProviderStaticCatalog : providerMocks . runProviderStaticCatalog ,
36+ } ) ) ;
37+
38+ import { getModelProviderRequestTransport } from "../provider-request-config.js" ;
39+ import {
40+ resolveBundledProviderStaticCatalogModel ,
41+ resolveBundledStaticCatalogModel ,
42+ } from "./model.static-catalog.js" ;
1943
2044function setManifestPlugins ( plugins : unknown [ ] ) {
2145 // Static catalog resolution reads scan metadata first, then loads the manifest
@@ -81,7 +105,17 @@ function createMistralManifestPlugin(overrides?: {
81105beforeEach ( ( ) => {
82106 manifestMocks . listOpenClawPluginManifestMetadata . mockReset ( ) ;
83107 manifestMocks . loadPluginManifest . mockReset ( ) ;
108+ providerMocks . normalizePluginDiscoveryResult . mockReset ( ) ;
109+ providerMocks . resolveBundledProviderCompatPluginIds . mockReset ( ) ;
110+ providerMocks . resolveOwningPluginIdsForProviderRef . mockReset ( ) ;
111+ providerMocks . resolveRuntimePluginDiscoveryProviders . mockReset ( ) ;
112+ providerMocks . runProviderStaticCatalog . mockReset ( ) ;
84113 setManifestPlugins ( [ ] ) ;
114+ providerMocks . resolveBundledProviderCompatPluginIds . mockReturnValue ( [ ] ) ;
115+ providerMocks . resolveOwningPluginIdsForProviderRef . mockReturnValue ( undefined ) ;
116+ providerMocks . resolveRuntimePluginDiscoveryProviders . mockResolvedValue ( [ ] ) ;
117+ providerMocks . runProviderStaticCatalog . mockResolvedValue ( undefined ) ;
118+ providerMocks . normalizePluginDiscoveryResult . mockReturnValue ( { } ) ;
85119} ) ;
86120
87121describe ( "resolveBundledStaticCatalogModel" , ( ) => {
@@ -166,3 +200,132 @@ describe("resolveBundledStaticCatalogModel", () => {
166200 ) . toBeUndefined ( ) ;
167201 } ) ;
168202} ) ;
203+
204+ describe ( "resolveBundledProviderStaticCatalogModel" , ( ) => {
205+ it ( "resolves exact rows from bundled provider static catalogs" , async ( ) => {
206+ const cfg = { plugins : { entries : { google : { enabled : true } } } } ;
207+ const provider = {
208+ id : "google" ,
209+ pluginId : "google" ,
210+ label : "Google" ,
211+ auth : [ ] ,
212+ staticCatalog : { run : vi . fn ( ) } ,
213+ } ;
214+ providerMocks . resolveOwningPluginIdsForProviderRef . mockReturnValue ( [ "google" ] ) ;
215+ providerMocks . resolveBundledProviderCompatPluginIds . mockReturnValue ( [ "google" ] ) ;
216+ providerMocks . resolveRuntimePluginDiscoveryProviders . mockResolvedValue ( [ provider ] ) ;
217+ providerMocks . runProviderStaticCatalog . mockResolvedValue ( { marker : "static-result" } ) ;
218+ providerMocks . normalizePluginDiscoveryResult . mockReturnValue ( {
219+ google : {
220+ api : "google-generative-ai" ,
221+ authHeader : true ,
222+ baseUrl : "https://generativelanguage.googleapis.com/v1beta" ,
223+ request : { headers : { "X-Static-Catalog" : "yes" } } ,
224+ models : [
225+ {
226+ id : "gemini-3.1-pro-preview" ,
227+ name : "Gemini 3.1 Pro Preview" ,
228+ reasoning : true ,
229+ input : [ "text" , "image" ] ,
230+ cost : { input : 2 , output : 12 , cacheRead : 0.5 , cacheWrite : 0 } ,
231+ contextWindow : 1_048_576 ,
232+ maxTokens : 65_536 ,
233+ mediaInput : { image : { maxSidePx : 3072 , tokenMode : "provider" } } ,
234+ } ,
235+ ] ,
236+ } ,
237+ } ) ;
238+
239+ const model = await resolveBundledProviderStaticCatalogModel ( {
240+ provider : "google" ,
241+ modelId : "gemini-3.1-pro-preview" ,
242+ cfg,
243+ } ) ;
244+
245+ expect ( model ) . toMatchObject ( {
246+ api : "google-generative-ai" ,
247+ authHeader : true ,
248+ baseUrl : "https://generativelanguage.googleapis.com/v1beta" ,
249+ contextTokens : undefined ,
250+ contextWindow : 1_048_576 ,
251+ cost : { input : 2 , output : 12 , cacheRead : 0.5 , cacheWrite : 0 } ,
252+ headers : { "X-Static-Catalog" : "yes" } ,
253+ id : "gemini-3.1-pro-preview" ,
254+ input : [ "text" , "image" ] ,
255+ maxTokens : 65_536 ,
256+ mediaInput : { image : { maxSidePx : 3072 , tokenMode : "provider" } } ,
257+ name : "Gemini 3.1 Pro Preview" ,
258+ provider : "google" ,
259+ reasoning : true ,
260+ } ) ;
261+ expect ( getModelProviderRequestTransport ( model ! ) ) . toEqual ( {
262+ headers : { "X-Static-Catalog" : "yes" } ,
263+ } ) ;
264+ expect ( providerMocks . resolveRuntimePluginDiscoveryProviders ) . toHaveBeenCalledWith ( {
265+ config : cfg ,
266+ workspaceDir : undefined ,
267+ env : process . env ,
268+ onlyPluginIds : [ "google" ] ,
269+ includeUntrustedWorkspacePlugins : false ,
270+ requireCompleteDiscoveryEntryCoverage : true ,
271+ discoveryEntriesOnly : true ,
272+ includeManifestModelCatalogProviders : false ,
273+ } ) ;
274+ expect ( providerMocks . runProviderStaticCatalog ) . toHaveBeenCalledWith ( {
275+ provider,
276+ config : cfg ,
277+ workspaceDir : undefined ,
278+ env : process . env ,
279+ } ) ;
280+ } ) ;
281+
282+ it ( "does not load provider catalogs when the provider owner is not bundled and enabled" , async ( ) => {
283+ providerMocks . resolveOwningPluginIdsForProviderRef . mockReturnValue ( [ "google" ] ) ;
284+ providerMocks . resolveBundledProviderCompatPluginIds . mockReturnValue ( [ ] ) ;
285+
286+ await expect (
287+ resolveBundledProviderStaticCatalogModel ( {
288+ provider : "google" ,
289+ modelId : "gemini-3.1-pro-preview" ,
290+ cfg : { } ,
291+ } ) ,
292+ ) . resolves . toBeUndefined ( ) ;
293+
294+ expect ( providerMocks . resolveRuntimePluginDiscoveryProviders ) . not . toHaveBeenCalled ( ) ;
295+ expect ( providerMocks . runProviderStaticCatalog ) . not . toHaveBeenCalled ( ) ;
296+ } ) ;
297+
298+ it ( "requires an exact provider and model match" , async ( ) => {
299+ const provider = { id : "google" , pluginId : "google" , label : "Google" , auth : [ ] } ;
300+ providerMocks . resolveOwningPluginIdsForProviderRef . mockReturnValue ( [ "google" ] ) ;
301+ providerMocks . resolveBundledProviderCompatPluginIds . mockReturnValue ( [ "google" ] ) ;
302+ providerMocks . resolveRuntimePluginDiscoveryProviders . mockResolvedValue ( [ provider ] ) ;
303+ providerMocks . normalizePluginDiscoveryResult . mockReturnValue ( {
304+ google : {
305+ api : "google-generative-ai" ,
306+ baseUrl : "https://generativelanguage.googleapis.com/v1beta" ,
307+ models : [ { id : "gemini-3.1-pro-preview" , name : "Gemini 3.1 Pro Preview" } ] ,
308+ } ,
309+ "google-vertex" : {
310+ api : "google-vertex" ,
311+ baseUrl : "https://aiplatform.googleapis.com/v1" ,
312+ models : [ { id : "gemini-3.1-pro-preview" , name : "Gemini 3.1 Pro Preview" } ] ,
313+ } ,
314+ } ) ;
315+
316+ await expect (
317+ resolveBundledProviderStaticCatalogModel ( {
318+ provider : "google" ,
319+ modelId : "gemini-2.5-pro" ,
320+ cfg : { } ,
321+ } ) ,
322+ ) . resolves . toBeUndefined ( ) ;
323+ await expect (
324+ resolveBundledProviderStaticCatalogModel ( {
325+ provider : "openrouter" ,
326+ modelId : "gemini-3.1-pro-preview" ,
327+ cfg : { } ,
328+ } ) ,
329+ ) . resolves . toBeUndefined ( ) ;
330+ } ) ;
331+ } ) ;
0 commit comments