@xbbg/langgraph 1.3.0 → 1.4.1

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.js CHANGED
@@ -51,6 +51,7 @@ var BLOOMBERG_TOOL_NAMES = [
51
51
  "xbbg_ext_cdx",
52
52
  "xbbg_ext_currency",
53
53
  "xbbg_ext_bql_builder",
54
+ "xbbg_ext_chart_spec",
54
55
  "xbbg_ext_market_session",
55
56
  "xbbg_ext_yas_overrides",
56
57
  "xbbg_ext_constants",
@@ -244,12 +245,12 @@ function rowCountOf(value) {
244
245
  if (typeof value !== "object" || value === null) {
245
246
  return null;
246
247
  }
247
- const record2 = value;
248
- const rowCount = record2.rowCount;
248
+ const record3 = value;
249
+ const rowCount = record3.rowCount;
249
250
  if (typeof rowCount === "number" && Number.isInteger(rowCount) && rowCount >= 0) {
250
251
  return rowCount;
251
252
  }
252
- const updateCount = record2.updateCount;
253
+ const updateCount = record3.updateCount;
253
254
  if (typeof updateCount === "number" && Number.isInteger(updateCount) && updateCount >= 0) {
254
255
  return updateCount;
255
256
  }
@@ -401,6 +402,389 @@ function createBloombergStructuredTool(func, fields) {
401
402
  );
402
403
  }
403
404
 
405
+ // src/chart-spec.ts
406
+ var VEGA_SCHEMA = "https://vega.github.io/schema/vega-lite/v5.json";
407
+ var COMPONENT_NAME = "xbbg_chart";
408
+ var X_FIELD_CANDIDATES = ["date", "time", "datetime", "timestamp"];
409
+ var LABEL_FIELD_CANDIDATES = ["ticker", "security", "member", "name", "label"];
410
+ var SERIES_FIELD_CANDIDATES = ["ticker", "security", "field", "side", "category"];
411
+ var VALUE_FIELD_CANDIDATES = [
412
+ "value",
413
+ "PX_LAST",
414
+ "close",
415
+ "price",
416
+ "weight",
417
+ "marketValue",
418
+ "market_value"
419
+ ];
420
+ var OPEN_FIELD_CANDIDATES = ["open", "OPEN", "PX_OPEN"];
421
+ var HIGH_FIELD_CANDIDATES = ["high", "HIGH", "PX_HIGH"];
422
+ var LOW_FIELD_CANDIDATES = ["low", "LOW", "PX_LOW"];
423
+ var CLOSE_FIELD_CANDIDATES = ["close", "CLOSE", "PX_LAST", "last", "value"];
424
+ var SIDE_FIELD_CANDIDATES = ["side", "SIDE", "type"];
425
+ var PRICE_FIELD_CANDIDATES = ["price", "PRICE", "px", "PX"];
426
+ var SIZE_FIELD_CANDIDATES = ["size", "SIZE", "quantity", "qty", "volume"];
427
+ function defaultChartForSource(source) {
428
+ switch (source) {
429
+ case "bdib":
430
+ return "candlestick";
431
+ case "depth":
432
+ return "depth";
433
+ case "holdings":
434
+ return "bar";
435
+ case "bdh":
436
+ case "rows":
437
+ return "line";
438
+ }
439
+ }
440
+ function fieldExists(rows, field) {
441
+ for (const row of rows) {
442
+ if (Object.prototype.hasOwnProperty.call(row, field)) {
443
+ return true;
444
+ }
445
+ }
446
+ return false;
447
+ }
448
+ function findCandidateField(rows, candidates) {
449
+ for (const candidate of candidates) {
450
+ if (fieldExists(rows, candidate)) {
451
+ return candidate;
452
+ }
453
+ }
454
+ const first = rows[0];
455
+ if (first === void 0) {
456
+ return void 0;
457
+ }
458
+ const keys = Object.keys(first);
459
+ for (const candidate of candidates) {
460
+ const lower = candidate.toLowerCase();
461
+ const match = keys.find((key) => key.toLowerCase() === lower);
462
+ if (match !== void 0 && fieldExists(rows, match)) {
463
+ return match;
464
+ }
465
+ }
466
+ return void 0;
467
+ }
468
+ function requireField(rows, field, label, candidates) {
469
+ const resolved = field ?? findCandidateField(rows, candidates);
470
+ if (resolved === void 0 || !fieldExists(rows, resolved)) {
471
+ throw new Error(
472
+ `Missing ${label}; pass ${label} explicitly or include one of: ${candidates.join(", ")}`
473
+ );
474
+ }
475
+ return resolved;
476
+ }
477
+ function hasFiniteNumber(rows, field) {
478
+ for (const row of rows) {
479
+ if (typeof row[field] === "number" && Number.isFinite(row[field])) {
480
+ return true;
481
+ }
482
+ }
483
+ return false;
484
+ }
485
+ function firstNumericField(rows, excludedField) {
486
+ const first = rows[0];
487
+ if (first === void 0) {
488
+ return void 0;
489
+ }
490
+ for (const key of Object.keys(first)) {
491
+ if (key !== excludedField && hasFiniteNumber(rows, key)) {
492
+ return key;
493
+ }
494
+ }
495
+ return void 0;
496
+ }
497
+ function requireNumericField(rows, field, label) {
498
+ if (!hasFiniteNumber(rows, field)) {
499
+ throw new Error(`${label} (${field}) must contain at least one finite numeric value`);
500
+ }
501
+ }
502
+ function inferVegaType(rows, field) {
503
+ for (const row of rows) {
504
+ const value = row[field];
505
+ if (typeof value === "number") {
506
+ return "quantitative";
507
+ }
508
+ if (typeof value === "string" && (/^\d{4}-\d{2}-\d{2}(?:$|[T\s])/u.test(value) || /^\d{8}$/u.test(value))) {
509
+ return "temporal";
510
+ }
511
+ }
512
+ return "nominal";
513
+ }
514
+ function normalizeTemporalRows(rows, field) {
515
+ let normalized;
516
+ for (let index = 0; index < rows.length; index += 1) {
517
+ const row = rows[index];
518
+ if (row === void 0) {
519
+ continue;
520
+ }
521
+ const value = row[field];
522
+ if (typeof value !== "string" || !/^\d{8}$/u.test(value)) {
523
+ normalized?.push(row);
524
+ continue;
525
+ }
526
+ normalized ??= rows.slice(0, index);
527
+ normalized.push({
528
+ ...row,
529
+ [field]: `${value.slice(0, 4)}-${value.slice(4, 6)}-${value.slice(6, 8)}`
530
+ });
531
+ }
532
+ return normalized ?? rows;
533
+ }
534
+ function tooltip(fields) {
535
+ return fields.map((field) => ({
536
+ field,
537
+ type: field === "_xbbg_value" ? "quantitative" : "nominal"
538
+ }));
539
+ }
540
+ function datumField(field) {
541
+ return `datum[${JSON.stringify(field)}]`;
542
+ }
543
+ function buildGenericSpec(input, rows, chart, title) {
544
+ const xField = requireField(rows, input.xField, "xField", X_FIELD_CANDIDATES);
545
+ let yFields;
546
+ if (input.yFields !== void 0) {
547
+ yFields = input.yFields;
548
+ } else {
549
+ const yField = findCandidateField(rows, VALUE_FIELD_CANDIDATES) ?? firstNumericField(rows, xField);
550
+ if (yField === void 0) {
551
+ throw new Error("Missing yFields; include at least one numeric value field");
552
+ }
553
+ yFields = [yField];
554
+ }
555
+ if (yFields.length === 0) {
556
+ throw new Error("Missing yFields; include at least one numeric value field");
557
+ }
558
+ for (const field of yFields) {
559
+ if (!fieldExists(rows, field)) {
560
+ throw new Error(`Missing y field: ${field}`);
561
+ }
562
+ requireNumericField(rows, field, "yField");
563
+ }
564
+ const normalizedRows = inferVegaType(rows, xField) === "temporal" ? normalizeTemporalRows(rows, xField) : rows;
565
+ const seriesField = input.seriesField ?? (yFields.length === 1 ? findCandidateField(rows, SERIES_FIELD_CANDIDATES) : void 0);
566
+ if (seriesField !== void 0 && !fieldExists(rows, seriesField)) {
567
+ throw new Error(`Missing series field: ${seriesField}`);
568
+ }
569
+ const mark = chart === "scatter" ? "point" : chart;
570
+ const encoding = {
571
+ x: { field: xField, title: xField, type: inferVegaType(normalizedRows, xField) }
572
+ };
573
+ const transform = [];
574
+ if (yFields.length === 1) {
575
+ const yField = yFields[0];
576
+ if (yField === void 0) {
577
+ throw new Error("Missing yFields; include at least one numeric value field");
578
+ }
579
+ encoding.y = { field: yField, title: yField, type: "quantitative" };
580
+ if (seriesField !== void 0) {
581
+ encoding.color = { field: seriesField, title: seriesField, type: "nominal" };
582
+ }
583
+ encoding.tooltip = tooltip([
584
+ xField,
585
+ ...seriesField === void 0 ? [] : [seriesField],
586
+ yField
587
+ ]);
588
+ } else {
589
+ transform.push({ as: ["_xbbg_series", "_xbbg_value"], fold: yFields });
590
+ encoding.y = { field: "_xbbg_value", title: "value", type: "quantitative" };
591
+ encoding.color = { field: "_xbbg_series", title: "series", type: "nominal" };
592
+ if (seriesField !== void 0) {
593
+ encoding.detail = { field: seriesField, type: "nominal" };
594
+ }
595
+ encoding.tooltip = tooltip([
596
+ xField,
597
+ ...seriesField === void 0 ? [] : [seriesField],
598
+ "_xbbg_series",
599
+ "_xbbg_value"
600
+ ]);
601
+ }
602
+ const spec = {
603
+ $schema: VEGA_SCHEMA,
604
+ data: { values: normalizedRows },
605
+ description: `xbbg ${chart} chart spec for ${input.source}`,
606
+ mark: { type: mark, tooltip: true },
607
+ title,
608
+ ...transform.length === 0 ? {} : { transform },
609
+ encoding
610
+ };
611
+ return { spec, xField, yFields, ...seriesField === void 0 ? {} : { seriesField } };
612
+ }
613
+ function buildBarSpec(input, rows, title) {
614
+ const xField = requireField(
615
+ rows,
616
+ input.xField ?? input.labelField,
617
+ "labelField",
618
+ LABEL_FIELD_CANDIDATES
619
+ );
620
+ const yField = requireField(
621
+ rows,
622
+ input.valueField ?? input.yFields?.[0],
623
+ "valueField",
624
+ VALUE_FIELD_CANDIDATES
625
+ );
626
+ requireNumericField(rows, yField, "valueField");
627
+ const seriesField = input.seriesField;
628
+ if (seriesField !== void 0 && !fieldExists(rows, seriesField)) {
629
+ throw new Error(`Missing series field: ${seriesField}`);
630
+ }
631
+ const encoding = {
632
+ x: { field: xField, sort: "-y", title: xField, type: inferVegaType(rows, xField) },
633
+ y: { field: yField, title: yField, type: "quantitative" },
634
+ tooltip: tooltip([xField, ...seriesField === void 0 ? [] : [seriesField], yField])
635
+ };
636
+ if (seriesField !== void 0) {
637
+ encoding.color = { field: seriesField, title: seriesField, type: "nominal" };
638
+ }
639
+ return {
640
+ spec: {
641
+ $schema: VEGA_SCHEMA,
642
+ data: { values: rows },
643
+ description: `xbbg bar chart spec for ${input.source}`,
644
+ encoding,
645
+ mark: { type: "bar", tooltip: true },
646
+ title
647
+ },
648
+ xField,
649
+ yFields: [yField],
650
+ ...seriesField === void 0 ? {} : { seriesField }
651
+ };
652
+ }
653
+ function buildCandlestickSpec(input, rows, title) {
654
+ const xField = requireField(rows, input.xField, "xField", X_FIELD_CANDIDATES);
655
+ const openField = requireField(rows, input.openField, "openField", OPEN_FIELD_CANDIDATES);
656
+ const highField = requireField(rows, input.highField, "highField", HIGH_FIELD_CANDIDATES);
657
+ const lowField = requireField(rows, input.lowField, "lowField", LOW_FIELD_CANDIDATES);
658
+ const closeField = requireField(rows, input.closeField, "closeField", CLOSE_FIELD_CANDIDATES);
659
+ for (const [label, field] of [
660
+ ["openField", openField],
661
+ ["highField", highField],
662
+ ["lowField", lowField],
663
+ ["closeField", closeField]
664
+ ]) {
665
+ requireNumericField(rows, field, label);
666
+ }
667
+ const normalizedRows = inferVegaType(rows, xField) === "temporal" ? normalizeTemporalRows(rows, xField) : rows;
668
+ const color = {
669
+ condition: { test: `${datumField(closeField)} >= ${datumField(openField)}`, value: "#137333" },
670
+ value: "#c5221f"
671
+ };
672
+ return {
673
+ spec: {
674
+ $schema: VEGA_SCHEMA,
675
+ data: { values: normalizedRows },
676
+ description: `xbbg candlestick chart spec for ${input.source}`,
677
+ encoding: {
678
+ x: { field: xField, title: xField, type: inferVegaType(normalizedRows, xField) }
679
+ },
680
+ layer: [
681
+ {
682
+ mark: "rule",
683
+ encoding: {
684
+ color,
685
+ tooltip: tooltip([xField, openField, highField, lowField, closeField]),
686
+ y: { field: lowField, title: "price", type: "quantitative" },
687
+ y2: { field: highField }
688
+ }
689
+ },
690
+ {
691
+ mark: "bar",
692
+ encoding: {
693
+ color,
694
+ y: { field: openField, title: "price", type: "quantitative" },
695
+ y2: { field: closeField }
696
+ }
697
+ }
698
+ ],
699
+ title
700
+ },
701
+ xField,
702
+ yFields: [openField, highField, lowField, closeField]
703
+ };
704
+ }
705
+ function buildDepthSpec(input, rows, title) {
706
+ const priceField = requireField(
707
+ rows,
708
+ input.priceField ?? input.xField,
709
+ "priceField",
710
+ PRICE_FIELD_CANDIDATES
711
+ );
712
+ const sizeField = requireField(
713
+ rows,
714
+ input.sizeField ?? input.valueField ?? input.yFields?.[0],
715
+ "sizeField",
716
+ SIZE_FIELD_CANDIDATES
717
+ );
718
+ const sideField = requireField(
719
+ rows,
720
+ input.sideField ?? input.seriesField,
721
+ "sideField",
722
+ SIDE_FIELD_CANDIDATES
723
+ );
724
+ requireNumericField(rows, priceField, "priceField");
725
+ requireNumericField(rows, sizeField, "sizeField");
726
+ return {
727
+ spec: {
728
+ $schema: VEGA_SCHEMA,
729
+ data: { values: rows },
730
+ description: `xbbg market depth chart spec for ${input.source}`,
731
+ encoding: {
732
+ color: { field: sideField, title: sideField, type: "nominal" },
733
+ tooltip: tooltip([sideField, priceField, sizeField]),
734
+ x: { field: priceField, title: priceField, type: "quantitative" },
735
+ y: { field: sizeField, title: sizeField, type: "quantitative" }
736
+ },
737
+ mark: { type: "bar", tooltip: true },
738
+ title
739
+ },
740
+ xField: priceField,
741
+ yFields: [sizeField],
742
+ seriesField: sideField
743
+ };
744
+ }
745
+ function createChartSpec(input) {
746
+ const maxPoints = input.maxPoints ?? input.rows.length;
747
+ const rows = input.rows.length > maxPoints ? input.rows.slice(0, maxPoints) : input.rows;
748
+ if (rows.length === 0) {
749
+ throw new Error("rows must contain at least one chart data row");
750
+ }
751
+ const chart = input.chart ?? defaultChartForSource(input.source);
752
+ const title = input.title ?? `${input.source} ${chart}`;
753
+ const warnings = [];
754
+ if (rows.length !== input.rows.length) {
755
+ warnings.push(
756
+ `Chart spec contains first ${rows.length} of ${input.rows.length} rows; narrow the upstream request for a complete visualization.`
757
+ );
758
+ }
759
+ const built = chart === "candlestick" ? buildCandlestickSpec(input, rows, title) : chart === "depth" ? buildDepthSpec(input, rows, title) : chart === "bar" ? buildBarSpec(input, rows, title) : buildGenericSpec(input, rows, chart, title);
760
+ const summary = {
761
+ chart,
762
+ inputRows: input.rows.length,
763
+ renderer: "vega-lite",
764
+ rowCount: rows.length,
765
+ source: input.source,
766
+ title,
767
+ truncatedInput: rows.length !== input.rows.length,
768
+ xField: built.xField,
769
+ yFields: built.yFields,
770
+ ...built.seriesField === void 0 ? {} : { seriesField: built.seriesField }
771
+ };
772
+ return {
773
+ kind: "xbbg.visualization",
774
+ version: 1,
775
+ component: COMPONENT_NAME,
776
+ renderer: "vega-lite",
777
+ rowCount: rows.length,
778
+ inputRowCount: input.rows.length,
779
+ truncatedInput: rows.length !== input.rows.length,
780
+ source: input.source,
781
+ chart,
782
+ summary,
783
+ spec: built.spec,
784
+ warnings
785
+ };
786
+ }
787
+
404
788
  // src/cdx-fields.ts
405
789
  var CDX_INFO_FIELDS = Object.freeze([
406
790
  "ROLLING_SERIES",
@@ -492,6 +876,7 @@ var OPTIONAL_EXTENSION_INSTRUCTIONS = [
492
876
  "- xbbg_ext_cdx: CDX ticker workflow support. Use parse_cdx_ticker to understand a CDX ticker, previous_cdx_series to roll back a series, cdx_gen_to_specific to resolve a generic CDX to a target series, and cdx_info/cdx_pricing/cdx_risk for predefined BDP field bundles. cdx_pricing and cdx_risk accept recoveryRate, which becomes the CDS_RR override.",
493
877
  "- xbbg_ext_currency: currency-planning helpers. build_fx_pair constructs the Bloomberg FX pair and conversion factor, same_currency avoids unnecessary conversion, and currencies_needing_conversion identifies which currencies differ from a target before requesting converted values.",
494
878
  "- xbbg_ext_bql_builder: safe BQL generators for common xbbg workflows. Use build_preferreds_query for preferred-stock discovery from an equity, build_corporate_bonds_query for company bond universes with optional currency/active filters, and build_etf_holdings_query for ETF constituents. Prefer these builders over hand-writing those BQL shapes.",
879
+ "- xbbg_ext_chart_spec: renderer-neutral chart spec helper. Convert bounded rows from xbbg_bdh, xbbg_bdib, holdings, depth, or already-shaped row data into a Vega-Lite JSON spec for frontend rendering; do not use it as proof that Bloomberg data was fetched.",
495
880
  "- xbbg_ext_market_session: exchange calendar/timezone support. derive_sessions turns day session times into session blocks, infer_timezone maps country codes to timezones, session_times_to_utc converts local sessions to UTC, get_market_rule gets MIC/exchange rules, default_turnover_dates and default_bqr_datetimes provide bounded defaults, and get/list_exchange_override inspect configured exchange metadata.",
496
881
  "- xbbg_ext_yas_overrides: builds flat YAS override maps for fixed-income BDP requests when the lower-level BDP workflow is required. Prefer xbbg_yas for actual YAS recipe fields.",
497
882
  "- xbbg_ext_constants: static lookup/format helpers for date parsing/formatting, futures month code/name mappings, dividend type mappings, and known dividend/ETF output columns.",
@@ -503,7 +888,7 @@ var OPTIONAL_LIMIT_INSTRUCTIONS = [
503
888
  "## Request limits and inputs",
504
889
  "- Keep Bloomberg requests bounded: explicit securities, explicit fields, explicit dates, limited rows, and no broad exploratory pulls unless the user narrows the universe.",
505
890
  "- Respect configured tool limits for securities, fields, rows, string size, BQL length, and search spec length. Ask the user to narrow the request rather than exceeding them.",
506
- "- Use flat primitive overrides and kwargs only: string, number, or boolean values. Do not send nested objects, arrays, or inferred defaults as overrides."
891
+ "- Use primitive kwargs only: string, number, or boolean values. For overrides on xbbg_bdp/xbbg_bdh/xbbg_bds, use primitive values for global overrides and nested primitive maps keyed by exact security for per-security overrides. Do not send other nested objects, arrays, or inferred defaults."
507
892
  ];
508
893
  var BLOOMBERG_TOOL_INSTRUCTIONS = [
509
894
  ...REQUIRED_TOOL_INSTRUCTIONS,
@@ -547,6 +932,7 @@ var EXT_FUTURES_DESCRIPTION = "Futures helpers for contract construction and sel
547
932
  var EXT_CDX_DESCRIPTION = "CDX helpers for parsing, series rolling/resolution, and predefined info/pricing/risk BDP field bundles.";
548
933
  var EXT_CURRENCY_DESCRIPTION = "Currency planning helpers: build FX pairs, test same-currency requests, and find currencies needing conversion.";
549
934
  var EXT_BQL_BUILDER_DESCRIPTION = "BQL builders for preferred stocks, corporate bonds, and ETF holdings. Prefer to construct those bounded BQL shapes before xbbg_bql.";
935
+ var EXT_CHART_SPEC_DESCRIPTION = "Renderer-neutral chart spec helper for frontend generative UI. Converts bounded Bloomberg rows from bdh, bdib, holdings, depth, or already-shaped row data into a Vega-Lite JSON spec; it does not fetch Bloomberg data or render images.";
550
936
  var EXT_MARKET_SESSION_DESCRIPTION = "Market session and timezone helpers for deriving sessions, UTC windows, market rules, exchange metadata, turnover defaults, and BQR datetime defaults.";
551
937
  var EXT_YAS_OVERRIDES_DESCRIPTION = "Build flat Bloomberg YAS override maps for fixed-income analytics fields.";
552
938
  var EXT_CONSTANTS_DESCRIPTION = "Static Bloomberg helper constants for date parsing/formatting, futures months, dividend types, and ETF/dividend columns.";
@@ -802,6 +1188,35 @@ function calculateSchema(options) {
802
1188
  }
803
1189
  });
804
1190
  }
1191
+ function chartSpecSchema(options) {
1192
+ const chartScalar = z__namespace.union([
1193
+ z__namespace.string().trim().max(options.maxStringChars),
1194
+ z__namespace.number(),
1195
+ z__namespace.boolean(),
1196
+ z__namespace.null()
1197
+ ]);
1198
+ const fieldName = nonEmptyString(options, "Input row field name.");
1199
+ return z__namespace.object({
1200
+ chart: z__namespace.enum(["line", "area", "bar", "scatter", "candlestick", "depth"]).optional().describe("Chart shape to generate. Defaults from source."),
1201
+ closeField: fieldName.optional().describe("Candlestick close-value field."),
1202
+ highField: fieldName.optional().describe("Candlestick high-value field."),
1203
+ labelField: fieldName.optional().describe("Categorical label field for bar charts."),
1204
+ lowField: fieldName.optional().describe("Candlestick low-value field."),
1205
+ maxPoints: z__namespace.number().int().positive().max(options.maxRows).optional().describe("Maximum rows to include in the frontend spec; defaults to all provided rows."),
1206
+ openField: fieldName.optional().describe("Candlestick open-value field."),
1207
+ priceField: fieldName.optional().describe("Market-depth price field."),
1208
+ renderer: z__namespace.literal("vega-lite").optional().describe("Visualization spec renderer. Currently only vega-lite is generated."),
1209
+ rows: z__namespace.array(z__namespace.record(chartScalar)).min(1).max(options.maxRows).describe("Chart data rows copied from a bounded Bloomberg tool result."),
1210
+ seriesField: fieldName.optional().describe("Optional series/color field."),
1211
+ sideField: fieldName.optional().describe("Market-depth bid/ask side field."),
1212
+ sizeField: fieldName.optional().describe("Market-depth size field."),
1213
+ source: z__namespace.enum(["bdh", "bdib", "holdings", "depth", "rows"]).describe("Bloomberg result shape that produced rows."),
1214
+ title: nonEmptyString(options, "Chart title.").optional(),
1215
+ valueField: fieldName.optional().describe("Primary numeric value field."),
1216
+ xField: fieldName.optional().describe("X-axis field."),
1217
+ yFields: stringArray(options, "Numeric value fields to plot.").optional()
1218
+ }).strict();
1219
+ }
805
1220
 
806
1221
  // src/ext-tools.ts
807
1222
  function resultString(resolver, name, value) {
@@ -816,6 +1231,7 @@ var EXT_TOOL_DEFINITIONS = Object.freeze([
816
1231
  { create: extCdxWithResolver, name: "xbbg_ext_cdx" },
817
1232
  { create: extCurrencyWithResolver, name: "xbbg_ext_currency" },
818
1233
  { create: extBqlBuilderWithResolver, name: "xbbg_ext_bql_builder" },
1234
+ { create: extChartSpecWithResolver, name: "xbbg_ext_chart_spec" },
819
1235
  { create: extMarketSessionWithResolver, name: "xbbg_ext_market_session" },
820
1236
  { create: extYasOverridesWithResolver, name: "xbbg_ext_yas_overrides" },
821
1237
  { create: extConstantsWithResolver, name: "xbbg_ext_constants" },
@@ -825,6 +1241,24 @@ var EXT_TOOL_DEFINITIONS = Object.freeze([
825
1241
  var BLOOMBERG_EXT_TOOL_NAMES = Object.freeze(
826
1242
  EXT_TOOL_DEFINITIONS.map((definition) => definition.name)
827
1243
  );
1244
+ function extChartSpecWithResolver(resolver) {
1245
+ const name = "xbbg_ext_chart_spec";
1246
+ return createBloombergStructuredTool(
1247
+ async (input) => {
1248
+ try {
1249
+ return await Promise.resolve(resultString(resolver, name, createChartSpec(input)));
1250
+ } catch (error) {
1251
+ throwWithToolContext(name, error);
1252
+ }
1253
+ },
1254
+ {
1255
+ responseFormat: "content_and_artifact",
1256
+ description: EXT_CHART_SPEC_DESCRIPTION,
1257
+ name,
1258
+ schema: chartSpecSchema(resolver.options)
1259
+ }
1260
+ );
1261
+ }
828
1262
  function extTickerWithResolver(resolver) {
829
1263
  const name = "xbbg_ext_ticker";
830
1264
  return createBloombergStructuredTool(
@@ -1238,6 +1672,9 @@ function createExtColumnsTool(options = {}) {
1238
1672
  function createExtCalculateTool(options = {}) {
1239
1673
  return extCalculateWithResolver(createCoreResolver(options));
1240
1674
  }
1675
+ function createExtChartSpecTool(options = {}) {
1676
+ return extChartSpecWithResolver(createCoreResolver(options));
1677
+ }
1241
1678
  function createBloombergExtToolsForResolver(resolver) {
1242
1679
  return EXT_TOOL_DEFINITIONS.filter(
1243
1680
  (definition) => !isToolDisabled(resolver.options, definition.name)
@@ -1383,6 +1820,60 @@ function primitiveMap(tool2, field) {
1383
1820
  return normalized;
1384
1821
  });
1385
1822
  }
1823
+ function overridesMap(tool2, field) {
1824
+ return z__namespace.record(
1825
+ z__namespace.string().min(1),
1826
+ z__namespace.union([primitiveSchema, z__namespace.record(z__namespace.string().min(1), primitiveSchema)])
1827
+ ).optional().transform((value, context) => {
1828
+ if (value === void 0) {
1829
+ return void 0;
1830
+ }
1831
+ const normalized = {};
1832
+ for (const [key, entry] of Object.entries(value)) {
1833
+ const normalizedKey = key.trim();
1834
+ if (normalizedKey.length === 0) {
1835
+ return normalizationIssue(context, tool2, field, new TypeError("contains an empty key"));
1836
+ }
1837
+ if (typeof entry !== "object") {
1838
+ if (typeof entry === "string" && entry.length === 0) {
1839
+ return normalizationIssue(
1840
+ context,
1841
+ tool2,
1842
+ field,
1843
+ new TypeError(`${normalizedKey} must not be an empty string`)
1844
+ );
1845
+ }
1846
+ normalized[normalizedKey] = entry;
1847
+ continue;
1848
+ }
1849
+ const normalizedOverrides = {};
1850
+ for (const [overrideKey, overrideValue] of Object.entries(entry)) {
1851
+ const normalizedOverrideKey = overrideKey.trim();
1852
+ if (normalizedOverrideKey.length === 0) {
1853
+ return normalizationIssue(
1854
+ context,
1855
+ tool2,
1856
+ field,
1857
+ new TypeError(`${normalizedKey} contains an empty override key`)
1858
+ );
1859
+ }
1860
+ if (typeof overrideValue === "string" && overrideValue.length === 0) {
1861
+ return normalizationIssue(
1862
+ context,
1863
+ tool2,
1864
+ field,
1865
+ new TypeError(
1866
+ `${normalizedKey}.${normalizedOverrideKey} must not be an empty string`
1867
+ )
1868
+ );
1869
+ }
1870
+ normalizedOverrides[normalizedOverrideKey] = overrideValue;
1871
+ }
1872
+ normalized[normalizedKey] = normalizedOverrides;
1873
+ }
1874
+ return normalized;
1875
+ });
1876
+ }
1386
1877
  function dateField(tool2, field) {
1387
1878
  return z__namespace.union([z__namespace.string(), z__namespace.number()]).transform((value, context) => {
1388
1879
  try {
@@ -1445,8 +1936,8 @@ function createBdpSchema(options) {
1445
1936
  kwargs: primitiveMap(tool2, "kwargs").describe(
1446
1937
  "Advanced Bloomberg request kwargs as flat string/number/boolean values only."
1447
1938
  ),
1448
- overrides: primitiveMap(tool2, "overrides").describe(
1449
- "Bloomberg field overrides as flat string/number/boolean values only."
1939
+ overrides: overridesMap(tool2, "overrides").describe(
1940
+ "Bloomberg field overrides. Use primitive values for global overrides and nested primitive maps keyed by exact security for per-security overrides."
1450
1941
  ),
1451
1942
  securities: stringArray2(
1452
1943
  tool2,
@@ -1477,8 +1968,8 @@ function createBdhSchema(options) {
1477
1968
  kwargs: primitiveMap(tool2, "kwargs").describe(
1478
1969
  "Advanced Bloomberg request kwargs as flat string/number/boolean values only."
1479
1970
  ),
1480
- overrides: primitiveMap(tool2, "overrides").describe(
1481
- "Bloomberg overrides as flat string/number/boolean values only."
1971
+ overrides: overridesMap(tool2, "overrides").describe(
1972
+ "Bloomberg overrides. Use primitive values for global overrides and nested primitive maps keyed by exact security for per-security overrides."
1482
1973
  ),
1483
1974
  securities: stringArray2(
1484
1975
  tool2,
@@ -1510,8 +2001,8 @@ function createBdsSchema(options) {
1510
2001
  kwargs: primitiveMap(tool2, "kwargs").describe(
1511
2002
  "Advanced Bloomberg request kwargs as flat string/number/boolean values only."
1512
2003
  ),
1513
- overrides: primitiveMap(tool2, "overrides").describe(
1514
- "Bloomberg overrides as flat string/number/boolean values only."
2004
+ overrides: overridesMap(tool2, "overrides").describe(
2005
+ "Bloomberg overrides. Use primitive values for global overrides and nested primitive maps keyed by exact security for per-security overrides."
1515
2006
  ),
1516
2007
  securities: stringArray2(
1517
2008
  tool2,
@@ -1731,7 +2222,7 @@ function createPreferredsSchema(options) {
1731
2222
  ).describe(
1732
2223
  "The issuer's common equity ticker as '<TICKER> <MARKET_SECTOR>', never a preferred ('Pfd') ticker and never a guessed one. Resolve a supplied ISIN/CUSIP with xbbg_resolve_isins first."
1733
2224
  ),
1734
- fields: stringArray2(tool2, "fields", options.maxFields, options.maxStringChars, '["<FIELD>"]').optional().describe("Optional fields to include in the preferreds recipe result.")
2225
+ fields: z__namespace.array(nonEmptyString2(tool2, "fields", options.maxStringChars, '["<FIELD>"]')).max(options.maxFields, `${tool2}: fields can contain at most ${options.maxFields} values`).transform((fields) => fields.length === 0 ? void 0 : fields).optional().describe("Optional fields to include in the preferreds recipe result.")
1735
2226
  });
1736
2227
  }
1737
2228
  function createCorporateBondsSchema(options) {
@@ -2051,14 +2542,15 @@ function bdpWithResolver(resolver) {
2051
2542
  async (input) => {
2052
2543
  try {
2053
2544
  const engine = await resolver.getEngine();
2054
- const result = await engine.bdp(input.securities, input.fields, {
2545
+ const options = {
2055
2546
  backend: "json",
2056
2547
  format: input.format,
2057
2548
  includeSecurityErrors: input.includeSecurityErrors,
2058
2549
  kwargs: input.kwargs,
2059
2550
  overrides: input.overrides,
2060
2551
  validateFields: validationSetting(resolver, input.validateFields)
2061
- });
2552
+ };
2553
+ const result = await engine.bdp(input.securities, input.fields, options);
2062
2554
  return resultString2(resolver, name, result);
2063
2555
  } catch (error) {
2064
2556
  throwWithToolContext(name, error);
@@ -2078,7 +2570,7 @@ function bdhWithResolver(resolver) {
2078
2570
  async (input) => {
2079
2571
  try {
2080
2572
  const engine = await resolver.getEngine();
2081
- const result = await engine.bdh(input.securities, input.fields, {
2573
+ const options = {
2082
2574
  backend: "json",
2083
2575
  end: input.end,
2084
2576
  format: input.format,
@@ -2086,7 +2578,8 @@ function bdhWithResolver(resolver) {
2086
2578
  overrides: input.overrides,
2087
2579
  start: input.start,
2088
2580
  validateFields: validationSetting(resolver, input.validateFields)
2089
- });
2581
+ };
2582
+ const result = await engine.bdh(input.securities, input.fields, options);
2090
2583
  return resultString2(resolver, name, result);
2091
2584
  } catch (error) {
2092
2585
  throwWithToolContext(name, error);
@@ -2106,12 +2599,13 @@ function bdsWithResolver(resolver) {
2106
2599
  async (input) => {
2107
2600
  try {
2108
2601
  const engine = await resolver.getEngine();
2109
- const result = await engine.bds(input.securities, [input.field], {
2602
+ const options = {
2110
2603
  backend: "json",
2111
2604
  kwargs: input.kwargs,
2112
2605
  overrides: input.overrides,
2113
2606
  validateFields: validationSetting(resolver, input.validateFields)
2114
- });
2607
+ };
2608
+ const result = await engine.bds(input.securities, [input.field], options);
2115
2609
  return resultString2(resolver, name, result);
2116
2610
  } catch (error) {
2117
2611
  throwWithToolContext(name, error);
@@ -2669,6 +3163,7 @@ exports.createEtfHoldingsTool = createEtfHoldingsTool;
2669
3163
  exports.createExtBqlBuilderTool = createExtBqlBuilderTool;
2670
3164
  exports.createExtCalculateTool = createExtCalculateTool;
2671
3165
  exports.createExtCdxTool = createExtCdxTool;
3166
+ exports.createExtChartSpecTool = createExtChartSpecTool;
2672
3167
  exports.createExtColumnsTool = createExtColumnsTool;
2673
3168
  exports.createExtConstantsTool = createExtConstantsTool;
2674
3169
  exports.createExtCurrencyTool = createExtCurrencyTool;