sync: update from private repo (6fcb0d0)
This commit is contained in:
@@ -9,6 +9,7 @@ import { PDFParse } from 'pdf-parse';
|
||||
import { ToolDef } from '../../llm/openai-compat.js';
|
||||
import type { ToolContext, ToolResult } from './core.js';
|
||||
import { resolveAndGuard, resolveOutputPathWithin, truncateToBudget, getToolOutputBudgetTokens } from './core.js';
|
||||
import { resolveThemePalette, extractSheetStyles } from './excel-styles.js';
|
||||
import { logger } from '../../logger.js';
|
||||
import { callVisionModel, resolveImagePath } from './image.js';
|
||||
import type {
|
||||
@@ -204,7 +205,7 @@ const READ_EXCEL_DEF: ToolDef = {
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'ReadExcel',
|
||||
description: 'Excel (.xlsx) を読み取りテキストで返す。詳細は ReadToolDoc({ name: "ReadExcel" })。',
|
||||
description: 'Excel (.xlsx) を読み取りテキストで返す。include_styles:true でセル装飾も取得可。詳細は ReadToolDoc({ name: "ReadExcel" })。',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
@@ -212,6 +213,8 @@ const READ_EXCEL_DEF: ToolDef = {
|
||||
sheet: { type: 'string', description: 'シート名(省略時は全シート)' },
|
||||
range: { type: 'string', description: 'セル範囲(例: A1:D10、省略時はシート全体)' },
|
||||
max_cells: { type: 'number', description: '最大セル数(デフォルト: 1000)' },
|
||||
include_styles: { type: 'boolean', description: 'true でセル装飾(背景色/フォント/罫線/書式/結合)を ### Styles として追記。デフォルト false' },
|
||||
max_style_ranges: { type: 'number', description: 'include_styles 時に出力する style range の上限(デフォルト 250)' },
|
||||
},
|
||||
required: ['path'],
|
||||
},
|
||||
@@ -538,6 +541,8 @@ async function executeReadExcel(
|
||||
const sheetFilter = typeof input['sheet'] === 'string' ? input['sheet'] : undefined;
|
||||
const rangeFilter = typeof input['range'] === 'string' ? input['range'] : undefined;
|
||||
const maxCells = typeof input['max_cells'] === 'number' ? input['max_cells'] : 1000;
|
||||
const includeStyles = input['include_styles'] === true;
|
||||
const maxStyleRanges = typeof input['max_style_ranges'] === 'number' ? input['max_style_ranges'] : 250;
|
||||
|
||||
let resolved: string;
|
||||
try {
|
||||
@@ -583,6 +588,9 @@ async function executeReadExcel(
|
||||
return { output: `Failed to read Excel file: ${(e as Error).message}`, isError: true };
|
||||
}
|
||||
|
||||
const themeXml = includeStyles ? ((wb.model as unknown as { themes?: { theme1?: string } }).themes?.theme1) : undefined;
|
||||
const palette = includeStyles ? resolveThemePalette(themeXml) : [];
|
||||
|
||||
const filename = path.basename(resolved);
|
||||
const sheetBlocks: ExcelSheetBlock[] = [];
|
||||
let totalCells = 0;
|
||||
@@ -697,6 +705,26 @@ async function executeReadExcel(
|
||||
parts.push('');
|
||||
}
|
||||
|
||||
if (includeStyles) {
|
||||
let rangeBudget = maxStyleRanges;
|
||||
for (const ws of wb.worksheets) {
|
||||
if (sheetFilter && ws.name !== sheetFilter) continue;
|
||||
const styleBounds = rangeFilter ? parseRange(rangeFilter) : null;
|
||||
const styles = extractSheetStyles(ws, palette, rangeBudget, styleBounds ?? undefined);
|
||||
rangeBudget -= styles.assignments.reduce((n, a) => n + a.ranges.length, 0);
|
||||
if (styles.legend.length === 0 && styles.merges.length === 0 && !styles.conditionalFormatting) continue;
|
||||
parts.push(`### Styles — ${ws.name}`);
|
||||
for (const { id, sig } of styles.legend) parts.push(`${id} = ${sig}`);
|
||||
parts.push('');
|
||||
for (const { id, ranges } of styles.assignments) parts.push(`${id}: ${ranges.join(',')}`);
|
||||
if (styles.merges.length) parts.push(`merges: ${styles.merges.join(',')}`);
|
||||
if (styles.conditionalFormatting) parts.push('conditionalFormatting: present (effective styles not evaluated)');
|
||||
if (styles.truncated || rangeBudget <= 0) parts.push(`[styles truncated: max_style_ranges=${maxStyleRanges} reached]`);
|
||||
parts.push('');
|
||||
if (rangeBudget <= 0) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (doc.warnings.length > 0) {
|
||||
parts.push('### Warnings');
|
||||
for (const w of doc.warnings) {
|
||||
|
||||
Reference in New Issue
Block a user