@versini/sassysaint-common 4.47.0 → 4.48.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 +80 -31
- package/dist/index.js +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -19,10 +19,26 @@ export declare enum ActionColor {
|
|
|
19
19
|
* List of all models available TODAY. This is used by the client to display the
|
|
20
20
|
* list of models.
|
|
21
21
|
*/
|
|
22
|
-
export declare const ALL_MODELS: readonly ["gpt-5.6-terra", "gpt-5.4-mini", "gpt-5.4-nano", "claude-opus-4-8", "claude-sonnet-5", "claude-haiku-4-5", "gemini-3-flash-
|
|
22
|
+
export declare const ALL_MODELS: readonly ["gpt-5.6-terra", "gpt-5.4-mini", "gpt-5.4-nano", "claude-opus-4-8", "claude-sonnet-5", "claude-haiku-4-5", "gemini-3.5-flash-lite", "gemini-3.6-flash", "gemini-3-pro-image"];
|
|
23
23
|
|
|
24
24
|
export declare const ALL_PROVIDERS: readonly ["OpenAI", "Anthropic", "Google"];
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Despite the name, this list has exactly one consumer (isReasoningModel, read
|
|
28
|
+
* by vercelEngine) and only affects Google: members get thinkingLevel, while
|
|
29
|
+
* non-members get the older numeric thinkingBudget. The OpenAI entries are
|
|
30
|
+
* inert because vercelEngine sets reasoningEffort unconditionally; the
|
|
31
|
+
* Anthropic entries are inert because nothing reads them at all (there is no
|
|
32
|
+
* anthropic key in that providerOptions object, so Anthropic extended thinking
|
|
33
|
+
* is NOT enabled by listing a model here).
|
|
34
|
+
*
|
|
35
|
+
* Both Google models are listed because Google documents thinking_level for
|
|
36
|
+
* both ids. This matters more than it looks: our explicit providerOptions
|
|
37
|
+
* overrides the SDK's own gemini-3 routing, so a Google model left out of this
|
|
38
|
+
* list is sent thinkingBudget even though the SDK would have picked
|
|
39
|
+
* thinkingLevel for it. Add any new Google model here.
|
|
40
|
+
*
|
|
41
|
+
*/
|
|
26
42
|
export declare const ALL_REASONING_MODELS: string[];
|
|
27
43
|
|
|
28
44
|
/**
|
|
@@ -219,17 +235,17 @@ export declare function getCompressionEmergencyThreshold(plan?: string): number;
|
|
|
219
235
|
* @example
|
|
220
236
|
* ```ts
|
|
221
237
|
* // User has multiple models per provider:
|
|
222
|
-
* getModelsGroupedByProvider([
|
|
238
|
+
* getModelsGroupedByProvider([MODEL_OPENAI_TOP, MODEL_OPENAI_MEDIUM, MODEL_ANTHROPIC_LOW]);
|
|
223
239
|
* // Returns: Map {
|
|
224
|
-
* // "OpenAI" => [
|
|
225
|
-
* // "Anthropic" => [
|
|
240
|
+
* // "OpenAI" => [MODEL_OPENAI_TOP, MODEL_OPENAI_MEDIUM],
|
|
241
|
+
* // "Anthropic" => [MODEL_ANTHROPIC_LOW]
|
|
226
242
|
* // }
|
|
227
243
|
*
|
|
228
244
|
* // Single model per provider:
|
|
229
|
-
* getModelsGroupedByProvider([
|
|
245
|
+
* getModelsGroupedByProvider([MODEL_OPENAI_TOP, MODEL_ANTHROPIC_LOW]);
|
|
230
246
|
* // Returns: Map {
|
|
231
|
-
* // "OpenAI" => [
|
|
232
|
-
* // "Anthropic" => [
|
|
247
|
+
* // "OpenAI" => [MODEL_OPENAI_TOP],
|
|
248
|
+
* // "Anthropic" => [MODEL_ANTHROPIC_LOW]
|
|
233
249
|
* // }
|
|
234
250
|
*
|
|
235
251
|
* // Empty models array:
|
|
@@ -257,12 +273,12 @@ export declare const getModelsGroupedByProvider: (models: string[]) => Map<strin
|
|
|
257
273
|
*
|
|
258
274
|
* @example
|
|
259
275
|
* ```ts
|
|
260
|
-
* // User's plan includes
|
|
261
|
-
* getProvidersFromModels([
|
|
262
|
-
* // Returns: [PROVIDER_OPENAI, PROVIDER_ANTHROPIC]
|
|
276
|
+
* // User's plan includes OpenAI, Anthropic and Google models:
|
|
277
|
+
* getProvidersFromModels([MODEL_OPENAI_LOW, MODEL_ANTHROPIC_LOW, MODEL_GOOGLE_MEDIUM]);
|
|
278
|
+
* // Returns: [PROVIDER_OPENAI, PROVIDER_ANTHROPIC, PROVIDER_GOOGLE]
|
|
263
279
|
*
|
|
264
|
-
* //
|
|
265
|
-
* getProvidersFromModels([
|
|
280
|
+
* // Google models removed from plan:
|
|
281
|
+
* getProvidersFromModels([MODEL_OPENAI_LOW, MODEL_ANTHROPIC_LOW]);
|
|
266
282
|
* // Returns: [PROVIDER_OPENAI, PROVIDER_ANTHROPIC]
|
|
267
283
|
* // (PROVIDER_GOOGLE is excluded)
|
|
268
284
|
*
|
|
@@ -304,15 +320,15 @@ export declare type HardCategory = (typeof HARD_CATEGORIES)[number];
|
|
|
304
320
|
* @example
|
|
305
321
|
* ```ts
|
|
306
322
|
* // Premium plan with multiple models per provider:
|
|
307
|
-
* hasMultipleModelsPerProvider([
|
|
323
|
+
* hasMultipleModelsPerProvider([MODEL_OPENAI_TOP, MODEL_OPENAI_MEDIUM, MODEL_ANTHROPIC_LOW]);
|
|
308
324
|
* // Returns: true (OpenAI has 2 models)
|
|
309
325
|
*
|
|
310
326
|
* // Plus plan with one model per provider:
|
|
311
|
-
* hasMultipleModelsPerProvider([
|
|
327
|
+
* hasMultipleModelsPerProvider([MODEL_OPENAI_MEDIUM, MODEL_ANTHROPIC_LOW, MODEL_GOOGLE_MEDIUM]);
|
|
312
328
|
* // Returns: false (each provider has only 1 model)
|
|
313
329
|
*
|
|
314
330
|
* // Basic plan with single model:
|
|
315
|
-
* hasMultipleModelsPerProvider([
|
|
331
|
+
* hasMultipleModelsPerProvider([MODEL_OPENAI_LOW]);
|
|
316
332
|
* // Returns: false
|
|
317
333
|
* ```
|
|
318
334
|
*
|
|
@@ -449,8 +465,8 @@ export declare function isRealProvider(value: unknown): value is RealProvider;
|
|
|
449
465
|
*
|
|
450
466
|
* @example
|
|
451
467
|
* ```ts
|
|
452
|
-
* isReasoningModel(
|
|
453
|
-
* isReasoningModel(
|
|
468
|
+
* isReasoningModel(MODEL_OPENAI_TOP); // Returns: true
|
|
469
|
+
* isReasoningModel(MODEL_GOOGLE_MEDIUM); // Returns: false
|
|
454
470
|
* ```
|
|
455
471
|
*
|
|
456
472
|
*/
|
|
@@ -481,13 +497,13 @@ export declare const MEMORY_CATEGORIES: readonly ["preference", "identity", "con
|
|
|
481
497
|
|
|
482
498
|
export declare type MemoryCategory = (typeof MEMORY_CATEGORIES)[number];
|
|
483
499
|
|
|
484
|
-
export declare const
|
|
500
|
+
export declare const MODEL_ANTHROPIC_LOW = "claude-haiku-4-5";
|
|
485
501
|
|
|
486
|
-
export declare const
|
|
502
|
+
export declare const MODEL_ANTHROPIC_MEDIUM = "claude-sonnet-5";
|
|
487
503
|
|
|
488
|
-
export declare const
|
|
504
|
+
export declare const MODEL_ANTHROPIC_TOP = "claude-opus-4-8";
|
|
489
505
|
|
|
490
|
-
export declare const
|
|
506
|
+
export declare const MODEL_CHAT_SUMMARY = "gpt-5.4-nano";
|
|
491
507
|
|
|
492
508
|
export declare const MODEL_DEV = "openai/gpt-oss-20b:free";
|
|
493
509
|
|
|
@@ -499,20 +515,20 @@ export declare const MODEL_DISPLAY_NAMES: Record<string, string>;
|
|
|
499
515
|
|
|
500
516
|
export declare const MODEL_EMBEDDING_TEXT = "text-embedding-3-small";
|
|
501
517
|
|
|
502
|
-
export declare const
|
|
518
|
+
export declare const MODEL_GENERATE_IMAGE = "gemini-3-pro-image";
|
|
503
519
|
|
|
504
|
-
export declare const
|
|
520
|
+
export declare const MODEL_GOOGLE_MEDIUM = "gemini-3.5-flash-lite";
|
|
505
521
|
|
|
506
|
-
export declare const
|
|
522
|
+
export declare const MODEL_GOOGLE_TOP = "gemini-3.6-flash";
|
|
507
523
|
|
|
508
524
|
/**
|
|
509
|
-
*
|
|
525
|
+
* The humanizer's rewrite pass needs a model that supports a high temperature
|
|
526
|
+
* and is fast; the Lite tier is too weak for it. It gets its own slot rather
|
|
527
|
+
* than borrowing a picker tier, so swapping the Google catalog can never
|
|
528
|
+
* silently move it (it previously rode MODEL_GOOGLE_MEDIUM and was downgraded
|
|
529
|
+
* to Flash-Lite by exactly that).
|
|
510
530
|
*/
|
|
511
|
-
export declare const
|
|
512
|
-
|
|
513
|
-
export declare const MODEL_GPT5_MINI = "gpt-5.4-mini";
|
|
514
|
-
|
|
515
|
-
export declare const MODEL_GPT5_NANO = "gpt-5.4-nano";
|
|
531
|
+
export declare const MODEL_HUMANIZE_REWRITE = "gemini-3.6-flash";
|
|
516
532
|
|
|
517
533
|
/**
|
|
518
534
|
* Bumped nano→mini (Track 2 T7): memory extraction now emits structured
|
|
@@ -521,6 +537,32 @@ export declare const MODEL_GPT5_NANO = "gpt-5.4-nano";
|
|
|
521
537
|
*/
|
|
522
538
|
export declare const MODEL_MEMORY_INFERENCE = "gpt-5.4-mini";
|
|
523
539
|
|
|
540
|
+
export declare const MODEL_OPENAI_LOW = "gpt-5.4-nano";
|
|
541
|
+
|
|
542
|
+
export declare const MODEL_OPENAI_MEDIUM = "gpt-5.4-mini";
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* List of available models TODAY.
|
|
546
|
+
*
|
|
547
|
+
* The user-selectable catalog models are named MODEL_<PROVIDER>_<TIER>, where
|
|
548
|
+
* the tier is relative WITHIN that provider rather than an absolute capability
|
|
549
|
+
* claim. Rungs fill from the top, so a provider offering two models uses TOP
|
|
550
|
+
* and MEDIUM and leaves LOW unused. The name is a stable slot: bumping a model
|
|
551
|
+
* swaps the value here (plus its display name, pricing row, and prefix match)
|
|
552
|
+
* and never renames the symbol, so a vendor moving its brand lines around
|
|
553
|
+
* cannot strand a name like MODEL_GEMINI_PRO on a Flash model.
|
|
554
|
+
*
|
|
555
|
+
* Two deliberate exceptions follow the catalog: the Perplexity ids are legacy
|
|
556
|
+
* and not user-selectable, and the role-purpose slots (MODEL_MEMORY_INFERENCE,
|
|
557
|
+
* MODEL_CHAT_SUMMARY, MODEL_SEARCH_DECOMPOSER, MODEL_HUMANIZE_REWRITE,
|
|
558
|
+
* MODEL_EMBEDDING_TEXT, MODEL_GENERATE_IMAGE, MODEL_TEACHER, MODEL_DEV) are
|
|
559
|
+
* named for the job they do rather than a provider tier, so a bump there never
|
|
560
|
+
* renames them either. Anything that needs a specific model for a specific job
|
|
561
|
+
* belongs in that group, NOT pointed at a picker tier whose value can move.
|
|
562
|
+
*
|
|
563
|
+
*/
|
|
564
|
+
export declare const MODEL_OPENAI_TOP = "gpt-5.6-terra";
|
|
565
|
+
|
|
524
566
|
/**
|
|
525
567
|
* Pseudo-model id for the Perplexity Search API (getWebSearch). Deliberately
|
|
526
568
|
* does NOT start with "sonar": the pricing prefix-fallback would otherwise bill
|
|
@@ -541,11 +583,18 @@ export declare const MODEL_TEACHER = "mistral-large-latest";
|
|
|
541
583
|
|
|
542
584
|
/**
|
|
543
585
|
* List of models available according to the providers.
|
|
586
|
+
*
|
|
587
|
+
* Array order is load-bearing and is NOT sorted by tier: index [0] is the
|
|
588
|
+
* per-provider fallback default in getModelNameForPlan (server providers.ts).
|
|
589
|
+
* Google deliberately defaults to MEDIUM, and Anthropic lists LOW before
|
|
590
|
+
* MEDIUM. Re-sorting these to match the TOP/MEDIUM/LOW names would silently
|
|
591
|
+
* change which model a user falls back to.
|
|
592
|
+
*
|
|
544
593
|
*/
|
|
545
594
|
export declare const MODELS_PER_PROVIDER: {
|
|
546
595
|
readonly OpenAI: readonly ["gpt-5.6-terra", "gpt-5.4-mini", "gpt-5.4-nano"];
|
|
547
596
|
readonly Anthropic: readonly ["claude-opus-4-8", "claude-haiku-4-5", "claude-sonnet-5"];
|
|
548
|
-
readonly Google: readonly ["gemini-3-flash-
|
|
597
|
+
readonly Google: readonly ["gemini-3.5-flash-lite", "gemini-3.6-flash"];
|
|
549
598
|
readonly Perplexity: readonly ["sonar", "sonar-pro", "perplexity-search"];
|
|
550
599
|
};
|
|
551
600
|
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
let e=["preference","identity","constraint","habit","bio","device","location","finance","professional","interest","style","behavior","other"],t=["identity","bio","constraint","professional"],E=e=>t.includes(e);var
|
|
1
|
+
let e=["preference","identity","constraint","habit","bio","device","location","finance","professional","interest","style","behavior","other"],t=["identity","bio","constraint","professional"],E=e=>t.includes(e);var O,n=((O={})[O.NewChat=0]="NewChat",O[O.Prompts=1]="Prompts",O[O.Attachment=2]="Attachment",O[O.PrivateChat=3]="PrivateChat",O[O.Send=4]="Send",O[O.Header=5]="Header",O[O.Provider=6]="Provider",O[O.Logo=7]="Logo",O[O.Placeholder=8]="Placeholder",O[O.Footer=9]="Footer",O);let r="system",i="user",o="assistant",a="OpenAI",_="Anthropic",R="Google",s="Summary",T="Memory",A="Perplexity",l="Mistral",M=a,I=[a,_,R],L="gpt-5.6-terra",P="gpt-5.4-mini",S="gpt-5.4-nano",g="claude-opus-4-8",N="claude-sonnet-5",D="claude-haiku-4-5",d="gemini-3.6-flash",c="gemini-3.5-flash-lite",u="sonar",C="sonar-pro",h="perplexity-search",p="openai/gpt-oss-20b:free",m="mistral-large-latest",G="gpt-5.4-mini",H="gpt-5.4-nano",f="gpt-5.4-mini",U="gemini-3.6-flash",y="text-embedding-3-small",Y="gemini-3-pro-image",B=5,b=1e4,F={[L]:"GPT-5.6 Terra",[P]:"GPT-5.4 Mini",[S]:"GPT-5.4 Nano",[g]:"Claude Opus 4.8",[N]:"Claude Sonnet 5",[D]:"Claude Haiku 4.5",[d]:"Gemini 3.6 Flash",[c]:"Gemini 3.5 Flash-Lite",[u]:"Sonar",[C]:"Sonar Pro",[h]:"Perplexity Search",[Y]:"Nano Banana 3 Pro",[m]:"Mistral Large"},v=[L,P,S,g,N,D,c,d,Y],w=[L,P,S,g,N,d,c],V={[a]:[L,P,S],[_]:[g,D,N],[R]:[c,d],[A]:[u,C,h]},X={[a]:[r,i,o],[_]:[i,o],[s]:[i,o],[T]:[i,o],[R]:[i,o],[A]:[i,o],[l]:[i,o]},W={[_]:["claude-opus-4","claude-sonnet-5","claude-sonnet-4","claude-haiku-4","claude-3"],[a]:["gpt-","o3","o4"],[R]:["gemini"],[A]:["sonar"],[l]:["mistral"]},x="x-diggidy-chat-id",k="x-diggidy-chat-timestamp",K="complete",z="timestamp",Z="tokenUsage",j="Diggidy",Q="sassy:basic",$="sassy:plus",q="sassy:advanced",J={TOOL:{BARCODE:"getProductByBarcode",BUNDLESIZE:"getBundleSize",DATETIME:"getDateTime",GENERATE_IMAGE:"generateImage",IMAGES:"getImages",NUTRITION_FACTS:"getNutritionFacts",POLLUTION:"getAirPollution",WEATHER:"getWeather",URLCONTENTPARSER:"getUrlContent",MEMORIES:"getUserMemories",HUMANIZE:"humanize",WEBSEARCH:"getWebSearch",GITREPOSITORY:"getGitRepoDetails",HOTELS:"getHotelDetails",PRONUNCIATION:"getPronunciation",FRENCH_TEACHINGS:"getFrenchTeachings"},ADDON:{ATTACHMENTS:"addon:attachments",CODEINTERPRETER:"addon:codeinterpreter",SHARE_CHAT:"addon:share-chat"}},ee={code_interpreter:"Running code...",web_search:"Searching the web...",[J.TOOL.WEATHER]:"Checking the weather...",[J.TOOL.POLLUTION]:"Fetching air pollution data...",[J.TOOL.BUNDLESIZE]:"Analyzing bundle size...",[J.TOOL.URLCONTENTPARSER]:"Reading a webpage...",[J.TOOL.WEBSEARCH]:"Searching the web...",[J.TOOL.GITREPOSITORY]:"Looking up repository...",[J.TOOL.HOTELS]:"Searching for hotels...",[J.TOOL.NUTRITION_FACTS]:"Fetching nutrition facts...",[J.TOOL.GENERATE_IMAGE]:"Generating image...",[J.TOOL.BARCODE]:"Looking up product...",[J.TOOL.PRONUNCIATION]:"Generating pronunciation...",[J.TOOL.HUMANIZE]:"Rewriting text...",[J.TOOL.IMAGES]:"Searching for images..."},et="Using tools...",eE=5e4,eO=1e5,en=8e5;function er(e){return e===q?en:eO}let ei=16384,eo=4,ea=3,e_="context-compression-summary",eR=new Set(["OpenAI",_,"Google",l]);function es(e){return"string"==typeof e&&eR.has(e)}let eT=/^auto \((.+)\)$/;function eA(e){let t=e.match(eT);return t?t[1]:e}let el=e=>{for(let[t,E]of Object.entries(W))if(E.some(t=>e.startsWith(t)))return t;return null},eM=e=>{if(!e||0===e.length)return[];let t=new Set;for(let E of e)for(let[e,O]of Object.entries(V))O.includes(E)&&("OpenAI"===e||e===_||"Google"===e)&&t.add(e);return I.filter(e=>t.has(e))},eI=e=>{let t=new Map;if(!e||0===e.length)return t;for(let E of e)for(let[e,O]of Object.entries(V))if(O.includes(E)){t.has(e)||t.set(e,[]),t.get(e)?.push(E);break}return t},eL=e=>{for(let t of eI(e).values())if(t.length>1)return!0;return!1},eP=(e,t)=>!!e&&!!t&&0!==t.length&&t.includes(e);function eS(e,t,E){if(!t||Array.isArray(t)&&0===t.length)return!0;let O=e instanceof Set?e:e?new Set(e):null;return!!O&&0!==O.size&&("string"==typeof t?O.has(t):E?.any===!0?t.some(e=>O.has(e)):t.every(e=>O.has(e)))}function eg(e){return w.includes(e)}export{v as ALL_MODELS,I as ALL_PROVIDERS,w as ALL_REASONING_MODELS,j as APPLICATION_NAME,W as APPROXIMATE_MODELS_PER_PROVIDER,J as CAPABILITIES,K as CHAT_COMPLETION_MARKER_KEY,eO as CONTEXT_COMPRESSION_EMERGENCY_THRESHOLD,en as CONTEXT_COMPRESSION_EMERGENCY_THRESHOLD_PREMIUM,ea as CONTEXT_COMPRESSION_KEEP_IMAGES,eo as CONTEXT_COMPRESSION_KEEP_RECENT,e_ as CONTEXT_COMPRESSION_SUMMARY_ID,eE as CONTEXT_COMPRESSION_THRESHOLD,M as DEFAULT_PROVIDER,et as DEFAULT_TOOL_LOADING_LABEL,x as DIGGIDY_CHAT_ID_HEADER,k as DIGGIDY_CHAT_TIMESTAMP_HEADER,t as HARD_CATEGORIES,ei as MAX_OUTPUT_TOKENS,B as MAX_SEARCH_QUERIES,e as MEMORY_CATEGORIES,V as MODELS_PER_PROVIDER,D as MODEL_ANTHROPIC_LOW,N as MODEL_ANTHROPIC_MEDIUM,g as MODEL_ANTHROPIC_TOP,H as MODEL_CHAT_SUMMARY,p as MODEL_DEV,F as MODEL_DISPLAY_NAMES,y as MODEL_EMBEDDING_TEXT,Y as MODEL_GENERATE_IMAGE,c as MODEL_GOOGLE_MEDIUM,d as MODEL_GOOGLE_TOP,U as MODEL_HUMANIZE_REWRITE,G as MODEL_MEMORY_INFERENCE,S as MODEL_OPENAI_LOW,P as MODEL_OPENAI_MEDIUM,L as MODEL_OPENAI_TOP,h as MODEL_PERPLEXITY_SEARCH,f as MODEL_SEARCH_DECOMPOSER,u as MODEL_SONAR,C as MODEL_SONAR_PRO,m as MODEL_TEACHER,Q as PLAN_BASIC,$ as PLAN_PLUS,q as PLAN_PREMIUM,_ as PROVIDER_ANTHROPIC,R as PROVIDER_GOOGLE,T as PROVIDER_MEMORY,l as PROVIDER_MISTRAL,a as PROVIDER_OPENAI,A as PROVIDER_PERPLEXITY,X as PROVIDER_ROLE_MAP,s as PROVIDER_SUMMARY,o as ROLE_ASSISTANT,r as ROLE_SYSTEM,i as ROLE_USER,b as SEARCH_QUERY_TIMEOUT_MS,z as SORT_BY_TIMESTAMP,Z as SORT_BY_TOKEN_USAGE,ee as TOOL_LOADING_LABELS,el as findProvider,er as getCompressionEmergencyThreshold,eI as getModelsGroupedByProvider,eM as getProvidersFromModels,eL as hasMultipleModelsPerProvider,eS as isEntitled,E as isHardCategory,eP as isModelAllowedForPlan,es as isRealProvider,eg as isReasoningModel,eA as stripAutoWrapper,n as ActionColor};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@versini/sassysaint-common",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.48.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Arno Versini",
|
|
6
6
|
"publishConfig": {
|
|
@@ -32,5 +32,5 @@
|
|
|
32
32
|
"test:watch": "vitest",
|
|
33
33
|
"watch": "npm-run-all dev"
|
|
34
34
|
},
|
|
35
|
-
"gitHead": "
|
|
35
|
+
"gitHead": "17673edb51a6d532a5fd1d5018245fbbf16c8cb7"
|
|
36
36
|
}
|