@broberg/ai-sdk 0.59.1 → 0.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +29 -8
- package/dist/index.js +75 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,8 @@ export { p as AlignedWordTimings, q as AvailabilitySource, r as AvailabilityStat
|
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
export { DEFAULT_CLIP_SEC, MEDIA_PRICING_CHECKED_AT, MediaPrice, MediaUnit, PRICING_STALE_AFTER_DAYS, PricingFreshness, UnitFreshness, getMediaPrice, listMediaPrices, pricingFreshness, pricingGeneratedAt } from './pricing.js';
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
/** `tavily` is CMS-legacy only (F078.5): reachable by explicit provider, never routed to. */
|
|
7
|
+
type SearchProviderId = "brave" | "cloudflare:ceramic" | "cloudflare:linkup" | "cloudflare:exa" | "tavily";
|
|
7
8
|
/** Why the search runs — picks the route and the cache lifetime (F078.2). */
|
|
8
9
|
type SearchPurpose = "agent" | "grounding" | "discovery" | "monitor";
|
|
9
10
|
interface SearchRequest {
|
|
@@ -47,6 +48,7 @@ interface SearchCredentials {
|
|
|
47
48
|
braveApiKey?: string;
|
|
48
49
|
cloudflareAccountId?: string;
|
|
49
50
|
cloudflareApiToken?: string;
|
|
51
|
+
tavilyApiKey?: string;
|
|
50
52
|
}
|
|
51
53
|
interface SearchCache {
|
|
52
54
|
get(key: string): SearchResult | undefined;
|
|
@@ -1882,8 +1884,9 @@ declare const aiConfigSchema: z.ZodObject<{
|
|
|
1882
1884
|
}>>;
|
|
1883
1885
|
}, "strip", z.ZodTypeAny, {
|
|
1884
1886
|
labels?: Record<string, string> | undefined;
|
|
1885
|
-
|
|
1887
|
+
costSink?: CostSink | null | undefined;
|
|
1886
1888
|
byok?: boolean | undefined;
|
|
1889
|
+
promptCache?: boolean | undefined;
|
|
1887
1890
|
strictPinning?: boolean | undefined;
|
|
1888
1891
|
defaults?: Partial<Record<"vision" | "video" | "embedding" | "fast" | "smart" | "powerful" | "cheap", {
|
|
1889
1892
|
provider: string;
|
|
@@ -1891,7 +1894,6 @@ declare const aiConfigSchema: z.ZodObject<{
|
|
|
1891
1894
|
transport: "http" | "subprocess";
|
|
1892
1895
|
}>> | undefined;
|
|
1893
1896
|
providers?: Record<string, ProviderAdapter> | undefined;
|
|
1894
|
-
costSink?: CostSink | null | undefined;
|
|
1895
1897
|
budget?: {
|
|
1896
1898
|
perCallUsd?: number | undefined;
|
|
1897
1899
|
rollingUsd?: number | undefined;
|
|
@@ -1902,8 +1904,9 @@ declare const aiConfigSchema: z.ZodObject<{
|
|
|
1902
1904
|
} | undefined;
|
|
1903
1905
|
}, {
|
|
1904
1906
|
labels?: Record<string, string> | undefined;
|
|
1905
|
-
|
|
1907
|
+
costSink?: CostSink | null | undefined;
|
|
1906
1908
|
byok?: boolean | undefined;
|
|
1909
|
+
promptCache?: boolean | undefined;
|
|
1907
1910
|
strictPinning?: boolean | undefined;
|
|
1908
1911
|
defaults?: Partial<Record<"vision" | "video" | "embedding" | "fast" | "smart" | "powerful" | "cheap", {
|
|
1909
1912
|
provider: string;
|
|
@@ -1911,7 +1914,6 @@ declare const aiConfigSchema: z.ZodObject<{
|
|
|
1911
1914
|
transport: "http" | "subprocess";
|
|
1912
1915
|
}>> | undefined;
|
|
1913
1916
|
providers?: Record<string, ProviderAdapter> | undefined;
|
|
1914
|
-
costSink?: CostSink | null | undefined;
|
|
1915
1917
|
budget?: {
|
|
1916
1918
|
perCallUsd?: number | undefined;
|
|
1917
1919
|
rollingUsd?: number | undefined;
|
|
@@ -2330,8 +2332,8 @@ declare const falStubAdapter: ProviderAdapter;
|
|
|
2330
2332
|
* wires the live adapters. */
|
|
2331
2333
|
declare const stubProviders: Record<string, ProviderAdapter>;
|
|
2332
2334
|
|
|
2333
|
-
declare const VERSION: "0.
|
|
2334
|
-
declare const SDK_TAG: "@broberg/ai-sdk@0.
|
|
2335
|
+
declare const VERSION: "0.61.0";
|
|
2336
|
+
declare const SDK_TAG: "@broberg/ai-sdk@0.61.0";
|
|
2335
2337
|
|
|
2336
2338
|
/** Built-in defaults. Every entry is overridable via AiConfig.defaults or a
|
|
2337
2339
|
* per-call override.
|
|
@@ -2858,6 +2860,25 @@ declare function streamTransport(req: StreamTransportRequest): AsyncIterable<str
|
|
|
2858
2860
|
|
|
2859
2861
|
declare const SEARCH_PRICE_USD: Record<SearchProviderId, number>;
|
|
2860
2862
|
|
|
2863
|
+
declare const webSearchTool: Tool;
|
|
2864
|
+
/** What the app decides — never the model. */
|
|
2865
|
+
interface WebSearchContext extends SearchOptions {
|
|
2866
|
+
/** Customer data may be in the query: only Zero-Data-Retention providers. */
|
|
2867
|
+
zdr?: boolean;
|
|
2868
|
+
/** Attribution, e.g. { tenantId } — also selects the tenant's daily cap. */
|
|
2869
|
+
labels?: Record<string, string>;
|
|
2870
|
+
}
|
|
2871
|
+
interface WebSearchToolResult {
|
|
2872
|
+
provider: string;
|
|
2873
|
+
results: {
|
|
2874
|
+
title: string;
|
|
2875
|
+
url: string;
|
|
2876
|
+
snippet: string;
|
|
2877
|
+
}[];
|
|
2878
|
+
}
|
|
2879
|
+
/** Run a `web_search` tool call. Throws on invalid arguments before any request. */
|
|
2880
|
+
declare function runWebSearch(args: Record<string, unknown>, ctx?: WebSearchContext): Promise<WebSearchToolResult>;
|
|
2881
|
+
|
|
2861
2882
|
interface SearchRoute {
|
|
2862
2883
|
/** Readable name — shows up in tests and in the reason a provider was picked. */
|
|
2863
2884
|
name: string;
|
|
@@ -2878,4 +2899,4 @@ declare function memorySearchCache(max?: number): SearchCache;
|
|
|
2878
2899
|
|
|
2879
2900
|
declare function search(req: SearchRequest, opts?: SearchOptions): Promise<SearchResult>;
|
|
2880
2901
|
|
|
2881
|
-
export { AZURE_DANISH_VOICES, AZURE_DANISH_VOICE_LIST, type AiClient, type AiConfig, type AzureVoiceInfo, BatchJob, BatchRequestItem, BatchResultItem, type BflAdapterConfig, type BflCredits, BudgetConfig, BudgetExceededError, BudgetGuard, BudgetStore, CONFIG_DERIVED_HOSTS, Capability, type ChatInput, ChatResult, ChatStreamEvent, type CheckVoiceOptions, type ClassifyInput, type ClassifyResult, type Contracts, type CostQuery, CostSink, type CostSummary, type CostSummaryQuery, type CostTimeseriesQuery, DEFAULT_BASE_URLS, DEFAULT_SEARCH_ROUTES, DEFAULT_TIER_MAP, type DesignInput, type DesignResult, type DiscordSinkConfig, ELEVENLABS_DANISH_VOICES, type EmbeddingInput, EmbeddingResult, type ExtractInput, type ExtractResult, type FalAdapterConfig, type FixedHostProvider, type HttpResponse, type ImageInput, ImageResult, type JudgeInput, JudgeResult, LOCALLY_PINNED_HOSTS, type MockupInput, type MockupResult, type ModerationInput, ModerationResult, type OcrInput, OcrResult, type OpenAICompatibleConfig, type PodcastInput, PodcastResult, type PriceDriftResult, type PriceDriftRow, type PricingEntry, ProviderAdapter, type RefreshOptions, type RefreshResult, Region, type RerankInput, type RerankResult, type RouteForecast, SDK_TAG, SEARCH_CACHE_TTL_MS, SEARCH_PRICE_USD, SearchBudgetExceededError, type SearchCache, type SearchCapStore, type SearchCredentials, type SearchItem, SearchKeyMissingError, type SearchOptions, type SearchProviderId, type SearchPurpose, type SearchRequest, type SearchResult, type SearchRoute, type SqliteBudgetStoreConfig, type SqliteSinkConfig, StreamHttpError, type SubprocessResponse, Tier, TierSpec, Tool, ToolCall, type TrainStyleInput, TrainStyleResult, type TranscribeInput, TranscribeResult, type TranslateInput, TranslateResult, Transport, type TransportRequest, type TransportResponse, type TtsInput, type UpmetricsCostClientConfig, UpmetricsCostError, type UpmetricsCostRow, type UpmetricsCostSummary, type UpmetricsCostTimeseries, type UpmetricsSink, type UpmetricsSinkConfig, type UpmetricsSinkStats, Usage, VERSION, type VideoInput, type VisionInput, type VoiceInfo, type VoiceProvider, type VoiceResolveResult, type VoiceStatus, VoiceUnavailableError, ZDR_PROVIDERS, aiConfigSchema, anthropicAdapter, anthropicApiAdapter, anthropicSubprocessAdapter, azureAdapter, bflAdapter, bflCredits, byokAdapter, chatInputSchema, checkPriceDrift, checkVoice, computeCost, createAI, deepinfraAdapter, deeplAdapter, deepseekAdapter, defaultBaseUrl, defaultProviders, discordSink, elevenlabsAdapter, embeddingInputSchema, falAdapter, falStubAdapter, freshUsage, fromProviderToolCall, geminiAdapter, getCostSummary, getPrice, httpTransport, imageInputSchema, isByokKeyed, listAzureDanishVoices, listVoices, makeContracts, makeOpenAICompatibleAdapter, matchLabel, memorySearchCache, messageSchema, mistralAdapter, mistralStubAdapter, multiSink, noopSink, openaiAdapter, openaiStubAdapter, openrouterAdapter, parseClaudeCliJson, parseJsonLoose, refreshAvailability, requestyAdapter, resetRefreshState, resetRegistry, resolveAzureVoice, resolveTier, resolveVoice, routeSearch, search, sqliteBudgetStore, sqliteSink, streamTransport, stubProviders, subprocessTransport, tierSpecSchema, toProviderTools, toolSchema, translateInputSchema, typesafeAdapter, upmetricsCostClient, upmetricsSink, usdFromMicro, vertexAdapter, visionInputSchema, wouldProviderRouteTo, wouldRouteTo };
|
|
2902
|
+
export { AZURE_DANISH_VOICES, AZURE_DANISH_VOICE_LIST, type AiClient, type AiConfig, type AzureVoiceInfo, BatchJob, BatchRequestItem, BatchResultItem, type BflAdapterConfig, type BflCredits, BudgetConfig, BudgetExceededError, BudgetGuard, BudgetStore, CONFIG_DERIVED_HOSTS, Capability, type ChatInput, ChatResult, ChatStreamEvent, type CheckVoiceOptions, type ClassifyInput, type ClassifyResult, type Contracts, type CostQuery, CostSink, type CostSummary, type CostSummaryQuery, type CostTimeseriesQuery, DEFAULT_BASE_URLS, DEFAULT_SEARCH_ROUTES, DEFAULT_TIER_MAP, type DesignInput, type DesignResult, type DiscordSinkConfig, ELEVENLABS_DANISH_VOICES, type EmbeddingInput, EmbeddingResult, type ExtractInput, type ExtractResult, type FalAdapterConfig, type FixedHostProvider, type HttpResponse, type ImageInput, ImageResult, type JudgeInput, JudgeResult, LOCALLY_PINNED_HOSTS, type MockupInput, type MockupResult, type ModerationInput, ModerationResult, type OcrInput, OcrResult, type OpenAICompatibleConfig, type PodcastInput, PodcastResult, type PriceDriftResult, type PriceDriftRow, type PricingEntry, ProviderAdapter, type RefreshOptions, type RefreshResult, Region, type RerankInput, type RerankResult, type RouteForecast, SDK_TAG, SEARCH_CACHE_TTL_MS, SEARCH_PRICE_USD, SearchBudgetExceededError, type SearchCache, type SearchCapStore, type SearchCredentials, type SearchItem, SearchKeyMissingError, type SearchOptions, type SearchProviderId, type SearchPurpose, type SearchRequest, type SearchResult, type SearchRoute, type SqliteBudgetStoreConfig, type SqliteSinkConfig, StreamHttpError, type SubprocessResponse, Tier, TierSpec, Tool, ToolCall, type TrainStyleInput, TrainStyleResult, type TranscribeInput, TranscribeResult, type TranslateInput, TranslateResult, Transport, type TransportRequest, type TransportResponse, type TtsInput, type UpmetricsCostClientConfig, UpmetricsCostError, type UpmetricsCostRow, type UpmetricsCostSummary, type UpmetricsCostTimeseries, type UpmetricsSink, type UpmetricsSinkConfig, type UpmetricsSinkStats, Usage, VERSION, type VideoInput, type VisionInput, type VoiceInfo, type VoiceProvider, type VoiceResolveResult, type VoiceStatus, VoiceUnavailableError, type WebSearchContext, type WebSearchToolResult, ZDR_PROVIDERS, aiConfigSchema, anthropicAdapter, anthropicApiAdapter, anthropicSubprocessAdapter, azureAdapter, bflAdapter, bflCredits, byokAdapter, chatInputSchema, checkPriceDrift, checkVoice, computeCost, createAI, deepinfraAdapter, deeplAdapter, deepseekAdapter, defaultBaseUrl, defaultProviders, discordSink, elevenlabsAdapter, embeddingInputSchema, falAdapter, falStubAdapter, freshUsage, fromProviderToolCall, geminiAdapter, getCostSummary, getPrice, httpTransport, imageInputSchema, isByokKeyed, listAzureDanishVoices, listVoices, makeContracts, makeOpenAICompatibleAdapter, matchLabel, memorySearchCache, messageSchema, mistralAdapter, mistralStubAdapter, multiSink, noopSink, openaiAdapter, openaiStubAdapter, openrouterAdapter, parseClaudeCliJson, parseJsonLoose, refreshAvailability, requestyAdapter, resetRefreshState, resetRegistry, resolveAzureVoice, resolveTier, resolveVoice, routeSearch, runWebSearch, search, sqliteBudgetStore, sqliteSink, streamTransport, stubProviders, subprocessTransport, tierSpecSchema, toProviderTools, toolSchema, translateInputSchema, typesafeAdapter, upmetricsCostClient, upmetricsSink, usdFromMicro, vertexAdapter, visionInputSchema, webSearchTool, wouldProviderRouteTo, wouldRouteTo };
|
package/dist/index.js
CHANGED
|
@@ -72,12 +72,37 @@ async function cloudflareSearch(req, creds, f) {
|
|
|
72
72
|
return { items, requestId: inner.metadata?.requestId };
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
// src/search/tavily.ts
|
|
76
|
+
async function tavilySearch(req, apiKey, f) {
|
|
77
|
+
const res = await f("https://api.tavily.com/search", {
|
|
78
|
+
method: "POST",
|
|
79
|
+
headers: { authorization: `Bearer ${apiKey}`, "content-type": "application/json" },
|
|
80
|
+
body: JSON.stringify({ query: req.query, max_results: Math.min(req.limit ?? 10, 20), search_depth: "basic" })
|
|
81
|
+
});
|
|
82
|
+
if (!res.ok) throw Object.assign(new Error(`tavily search: HTTP ${res.status} ${(await res.text()).slice(0, 200)}`), { status: res.status });
|
|
83
|
+
const body = await res.json();
|
|
84
|
+
const items = (body.results ?? []).map((r) => {
|
|
85
|
+
const published = r.published_date ? new Date(r.published_date) : void 0;
|
|
86
|
+
return {
|
|
87
|
+
title: r.title ?? "",
|
|
88
|
+
url: r.url ?? "",
|
|
89
|
+
description: r.content ?? "",
|
|
90
|
+
provider: "tavily",
|
|
91
|
+
...published && !Number.isNaN(published.getTime()) ? { publishedAt: published.toISOString() } : {}
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
return { items, ...body.request_id ? { requestId: body.request_id } : {} };
|
|
95
|
+
}
|
|
96
|
+
|
|
75
97
|
// src/search/prices.ts
|
|
76
98
|
var SEARCH_PRICE_USD = {
|
|
77
99
|
"cloudflare:ceramic": 25e-5,
|
|
78
100
|
"cloudflare:linkup": 5e-3,
|
|
79
101
|
"cloudflare:exa": 7e-3,
|
|
80
|
-
brave: 5e-3
|
|
102
|
+
brave: 5e-3,
|
|
103
|
+
// 1 credit per basic search; $0.0075/credit on the entry plan, less on bigger plans
|
|
104
|
+
// (docs.tavily.com/documentation/api-credits, 2026-10-09).
|
|
105
|
+
tavily: 75e-4
|
|
81
106
|
};
|
|
82
107
|
|
|
83
108
|
// src/search/router.ts
|
|
@@ -102,7 +127,7 @@ function routeSearch(req, routes = DEFAULT_SEARCH_ROUTES) {
|
|
|
102
127
|
(r) => (r.when.fresh === void 0 || r.when.fresh === fresh) && (r.when.english === void 0 || r.when.english === english) && (r.when.purpose === void 0 || r.when.purpose.includes(req.purpose ?? "agent"))
|
|
103
128
|
) ?? routes[routes.length - 1];
|
|
104
129
|
let chain = route.chain.filter((p) => english || p !== "cloudflare:ceramic");
|
|
105
|
-
chain = chain.filter((p) => p !== "cloudflare:exa");
|
|
130
|
+
chain = chain.filter((p) => p !== "cloudflare:exa" && p !== "tavily");
|
|
106
131
|
if (req.zdr) chain = chain.filter((p) => ZDR_PROVIDERS.has(p));
|
|
107
132
|
return chain;
|
|
108
133
|
}
|
|
@@ -188,6 +213,45 @@ var SearchBudgetExceededError = class extends Error {
|
|
|
188
213
|
requestedUsd;
|
|
189
214
|
};
|
|
190
215
|
|
|
216
|
+
// src/search/tool.ts
|
|
217
|
+
var PURPOSES = ["agent", "grounding", "discovery", "monitor"];
|
|
218
|
+
var webSearchTool = {
|
|
219
|
+
name: "web_search",
|
|
220
|
+
description: "Search the open web. Returns up to 10 results with title, url and snippet. Set lang to the query's language (e.g. 'da', 'en'). Set fresh when the answer must be recent (news, prices, events).",
|
|
221
|
+
parameters: {
|
|
222
|
+
type: "object",
|
|
223
|
+
properties: {
|
|
224
|
+
query: { type: "string", minLength: 1, maxLength: 1024, description: "What to search for." },
|
|
225
|
+
lang: { type: "string", description: "Language of the query, e.g. 'da' or 'en'." },
|
|
226
|
+
limit: { type: "integer", minimum: 1, maximum: 10, description: "Number of results (default 10)." },
|
|
227
|
+
purpose: { type: "string", enum: PURPOSES, description: "agent (default), grounding, discovery or monitor." },
|
|
228
|
+
fresh: { type: "boolean", description: "Results must be recent." }
|
|
229
|
+
},
|
|
230
|
+
required: ["query"],
|
|
231
|
+
additionalProperties: false
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
async function runWebSearch(args, ctx = {}) {
|
|
235
|
+
const { query, lang, limit, purpose, fresh } = args;
|
|
236
|
+
if (typeof query !== "string" || query.trim() === "") throw new Error("web_search: `query` must be a non-empty string");
|
|
237
|
+
if (lang !== void 0 && typeof lang !== "string") throw new Error("web_search: `lang` must be a string");
|
|
238
|
+
if (limit !== void 0 && (!Number.isInteger(limit) || limit < 1 || limit > 10)) throw new Error("web_search: `limit` must be an integer 1\u201310");
|
|
239
|
+
if (purpose !== void 0 && !PURPOSES.includes(purpose)) throw new Error(`web_search: \`purpose\` must be one of ${PURPOSES.join(", ")}`);
|
|
240
|
+
if (fresh !== void 0 && typeof fresh !== "boolean") throw new Error("web_search: `fresh` must be a boolean");
|
|
241
|
+
const { zdr, labels, ...opts } = ctx;
|
|
242
|
+
const req = {
|
|
243
|
+
query,
|
|
244
|
+
...lang !== void 0 ? { lang } : {},
|
|
245
|
+
...limit !== void 0 ? { limit } : {},
|
|
246
|
+
...purpose !== void 0 ? { purpose } : {},
|
|
247
|
+
...fresh !== void 0 ? { fresh } : {},
|
|
248
|
+
...zdr ? { zdr } : {},
|
|
249
|
+
...labels ? { labels } : {}
|
|
250
|
+
};
|
|
251
|
+
const r = await search(req, opts);
|
|
252
|
+
return { provider: r.meta.provider, results: r.items.map((i) => ({ title: i.title, url: i.url, snippet: i.description })) };
|
|
253
|
+
}
|
|
254
|
+
|
|
191
255
|
// src/search/index.ts
|
|
192
256
|
var sharedCache = memorySearchCache();
|
|
193
257
|
var sharedCapStore = /* @__PURE__ */ (() => {
|
|
@@ -216,6 +280,11 @@ async function callProvider(provider, req, opts) {
|
|
|
216
280
|
if (!key) throw new SearchKeyMissingError(provider, "BRAVE_API_KEY");
|
|
217
281
|
return braveSearch({ ...req, provider }, key, f);
|
|
218
282
|
}
|
|
283
|
+
if (provider === "tavily") {
|
|
284
|
+
const key = c.tavilyApiKey ?? env.TAVILY_API_KEY;
|
|
285
|
+
if (!key) throw new SearchKeyMissingError(provider, "TAVILY_API_KEY");
|
|
286
|
+
return tavilySearch({ ...req, provider }, key, f);
|
|
287
|
+
}
|
|
219
288
|
if (provider.startsWith("cloudflare:")) {
|
|
220
289
|
const accountId = c.cloudflareAccountId ?? env.CLOUDFLARE_ACCOUNT_ID;
|
|
221
290
|
const apiToken = c.cloudflareApiToken ?? env.CLOUDFLARE_API_TOKEN;
|
|
@@ -3829,8 +3898,8 @@ var aiConfigSchema = z.object({
|
|
|
3829
3898
|
import { randomUUID } from "crypto";
|
|
3830
3899
|
|
|
3831
3900
|
// src/version.ts
|
|
3832
|
-
var VERSION = "0.
|
|
3833
|
-
var SDK_TAG = "@broberg/ai-sdk@0.
|
|
3901
|
+
var VERSION = "0.61.0";
|
|
3902
|
+
var SDK_TAG = "@broberg/ai-sdk@0.61.0";
|
|
3834
3903
|
|
|
3835
3904
|
// src/cost/sinks/upmetrics.ts
|
|
3836
3905
|
function isRetryableStatus(status) {
|
|
@@ -5447,6 +5516,7 @@ export {
|
|
|
5447
5516
|
resolveTier,
|
|
5448
5517
|
resolveVoice,
|
|
5449
5518
|
routeSearch,
|
|
5519
|
+
runWebSearch,
|
|
5450
5520
|
search,
|
|
5451
5521
|
sqliteBudgetStore,
|
|
5452
5522
|
sqliteSink,
|
|
@@ -5463,6 +5533,7 @@ export {
|
|
|
5463
5533
|
usdFromMicro,
|
|
5464
5534
|
vertexAdapter,
|
|
5465
5535
|
visionInputSchema,
|
|
5536
|
+
webSearchTool,
|
|
5466
5537
|
wouldProviderRouteTo,
|
|
5467
5538
|
wouldRouteTo
|
|
5468
5539
|
};
|