@@ -67,12 +67,18 @@ const CATALOG = {
|
67 | 67 | requestFormat: "google.generate_content", |
68 | 68 | responseFormat: "google.generate_content", |
69 | 69 | }, |
| 70 | +{ |
| 71 | +path: "/v1beta/models/${model}:streamGenerateContent", |
| 72 | +methods: ["POST"], |
| 73 | +requestFormat: "google.generate_content", |
| 74 | +responseFormat: "google.generate_content", |
| 75 | +}, |
70 | 76 | ], |
71 | 77 | models: [ |
72 | 78 | { |
73 | 79 | id: "google/gemini-default", |
74 | 80 | upstream: "gemini", |
75 | | -capabilities: ["llm.generate"], |
| 81 | +capabilities: ["llm.generate", "llm.stream"], |
76 | 82 | }, |
77 | 83 | ], |
78 | 84 | }, |
@@ -200,6 +206,23 @@ describe("clawrouter provider catalog", () => {
|
200 | 206 | expect((headers as Headers).get("authorization")).toBe("Bearer clawrouter-test-key"); |
201 | 207 | }); |
202 | 208 | |
| 209 | +it("does not advertise Gemini models without an explicit streaming route", async () => { |
| 210 | +const generateOnlyCatalog = structuredClone(CATALOG); |
| 211 | +generateOnlyCatalog.providers[2].routes = generateOnlyCatalog.providers[2].routes.filter( |
| 212 | +(route) => !route.path.includes(":streamGenerateContent"), |
| 213 | +); |
| 214 | +generateOnlyCatalog.providers[2].models[0].capabilities = ["llm.generate"]; |
| 215 | +const { fetchGuard } = buildFetchGuard(generateOnlyCatalog); |
| 216 | + |
| 217 | +const provider = await buildClawRouterProviderConfig({ |
| 218 | +apiKey: "clawrouter-test-key", |
| 219 | +baseUrl: "https://clawrouter.example", |
| 220 | + fetchGuard, |
| 221 | +}); |
| 222 | + |
| 223 | +expect(provider.models.map((model) => model.id)).not.toContain("google/gemini-default"); |
| 224 | +}); |
| 225 | + |
203 | 226 | it("keeps credential-scoped route metadata isolated on each catalog result", async () => { |
204 | 227 | const firstCatalog = structuredClone(CATALOG); |
205 | 228 | firstCatalog.providers[1].models[0].upstream = "first-upstream"; |
|