| Age | Commit message (Collapse) | Author |
|
MCP Tool結果をテキストと型付きartifactへ分離し、画像・音声のbase64をMIME、サイズ、件数のallowlistで検証する。annotations.audienceを反映し、ユーザー向けartifactだけをセッションへ保存してSSEにはメタデータのみを公開する。
画像はvision capabilityが明示されたモデルだけへ渡し、非対応時は既存の多言語プロンプト機構による固定通知へ置き換える。MCP生成音声とユーザー添付音声はOllamaへ送らず、誤デコードしたテキストやbase64がモデル入力へ混入しないポリシーに統一する。
保存メディア用の認証済み同一オリジン配信ルートを追加し、セッションスコープ、MIME allowlist、nosniff、private/no-store、Rangeリクエストを適用する。UIはMCP由来HTMLを使わずDOM APIでimgとaudio controlsを生成し、CSPへmedia-src selfを追加する。
web_searchは文字列メタデータだけを返す既存仕様を維持する。scrape_urlとsearch_and_fetchは画像・音声・動画レスポンスを拒否し、search_and_fetchの対象拡張子を明示的allowlistへ変更して標準Webツールをテキスト専用の契約に揃える。
責務境界と導入しない過剰対策をdocs/multimedia.mdへ記録し、README、ツール仕様、CHANGELOGを更新する。MCP artifact変換・永続化、vision分岐、音声非送信、認証、Range配信、MIME偽装拒否をテストで追加確認した。
Validation: 148 tests passed; ruff check passed; mypy src passed; node --check src/ollama_web/static/app.js passed.
|
|
長いPDFを複数会話に分けて取得する際、モデルがurl_or_pathを省略したり、過去の添付IDや古い継続トークンを組み合わせたりして、先頭または以前のページへ戻る問題を改善する。PDFカーソル自体は正しく進んでいたため、汎用LLM層やセッション形式を変更せず、pdf_to_textの入出力契約だけを明確化する。
- continue_fromを自己完結した再開情報として扱い、PDFソース、ページ番号、ページ内文字位置をトークンから復元
- 継続時はurl_or_pathを省略可能にし、競合する値が渡されてもトークン内のソースを優先
- Ollama生成スキーマでもurl_or_pathを任意にして、continue_from単独のツール呼び出しを許可
- 未完了結果へ最新トークン、旧トークンの置換、単独再開、上限時の保持を表すPDF専用メタデータを追加
- 継続メタデータを固定キーと真偽値で表現し、会話言語に依存しない形式へ統一
- 無効トークン時は先頭再試行を禁止し、直前の最新トークンを再利用する機械可読情報を返却
- メタデータをpage-info側へ配置して本文連結を汚さず、結果全体を6000文字以内に再調整
- llm.py、チャット処理、セッションJSON、scrape_url、search_and_fetch、MCPツールは変更しない
検証:
- pytest: 132 passed
- ruff check: passed
- mypy src: passed
- 1463891.1463912.pdf: 12ページ、52577文字をcontinue_from単独の10回で完全再構成
- 指定PDFの最大ツール結果長はメタデータ込み5992文字
|
|
PDF本文をページ単位で抽出した後に文字数上限で切断しつつ、継続位置だけ次ページへ進めていたため、ページ末尾が復元不能になる問題を修正する。ツール側の本文上限とLLM側の結果上限の不整合、25ページの処理上限、添付経路での継続情報欠落も共通契約で解消する。
- 共通PageResultと、メタデータ込みの結果上限内で本文量を決めるフォーマッターを追加
- v2トークンにPDFのページ番号とページ内文字位置を保持し、v1トークンの読み取り互換性を維持
- pdf_to_text、scrape_url、search_and_fetchを返却済み本文だけカーソルが進む方式へ統一
- max_pdf_pagesを文書全体ではなく1呼び出しの処理上限として扱い、長い単一ページと26ページ目以降の継続取得に対応
- attachment:<file_id>をセッション限定で解決し、既存添付も保存済みプレビューではなく元PDFから再解析
- Web取得ファイルをセッションとURL単位で重複排除し、HTMLおよびテキスト経路の全文ページングも維持
- 正常なページング結果の後段切断を禁止し、結果上限・コンテキスト予算・ラウンド上限では未取得状態を明示
- 日英プロンプト、UIメッセージ、ツールスキーマ、変更履歴を実装へ同期
- 実PDF、30ページ超、長い単一ページ、旧トークン、セッション分離、コンテキスト境界の回帰テストを追加
検証:
- pytest: 130 passed
- ruff check src tests: passed
- mypy src: passed
- c2144.pdf: 19ページ、抽出本文46015文字(ページ区切り込み46051文字)を8回で重複・欠落なく完全再構成
|
|
## 背景
search_and_fetch の next_token / continue_from によるページング継続が
PDF 形式で間欠的に失敗するバグが報告された。発生エラー:
1. payload is not valid JSON: Expecting ':' delimiter
2. 'utf-8' codec can't decode byte 0xda in position 278
1ページ目の取得自体は成功し、next_token 返却まで正常だが、
next_token を continue_from に渡した2ページ目でエラーが発生。
txt/md/csv/json では安定動作し、PDF で間欠的・再現性なし。
## 根本原因
コード精査の結果、報告の「非UTF-8バイト混入」仮説に対し、より有力な
主因として「トークン巨大化による末尾欠損」を特定:
- _render_file_page() は matched 配列(title/href/body 全部入り)を
トークンに埋め込んでいたが、レンダリングで使うのは title と href
のみ。body(DuckDuckGo のスニペット、PDF 論文要約で数KB)は未使用
- 候補複数件でトークンが数KB〜十数KBに膨張
- LLM が continue_from にコピーする際、コンテキスト長や
_trim_tool_result の切り詰めでトークン末尾が欠損
- base64 デコード結果が不正になり JSONDecodeError(JSON途中切断)
と UnicodeDecodeError(マルチバイト境界切断)の両エラーが発生
- PDF で間欠的・再現性なしは、検索結果セットごとにスニペット長が
変動しトークンサイズが破損閾値を前後するためと説明がつく
補助要因: make_token() の ensure_ascii=False が、末尾欠損時に
マルチバイト文字境界で切れる確率を上げていた。
## 修正内容(案A: ユーザー承認済み)
1. pagination.py の make_token() を ensure_ascii=True 化
- トークンを純ASCII化し、末尾欠損でも UnicodeDecodeError を防止
- 非ASCII文字は \uXXXX エスケープ(6バイト固定)で安定化
2. fetch.py に _slim_matched() ヘルパーを追加
- matched から body を除外し title+href のみ埋め込み
- 3箇所の make_token 呼び出し(fetch失敗時・ファイル内継続時・
次ファイル移行時)を matched=_slim_matched(matched) に変更
- トークンサイズを数KB〜十数KB → 数百バイトに削減
3. トークン形式バージョン v は 1維持(構造不変・後方互換)
- 旧トークン(body入り・ensure_ascii=False)も parse_token で
正常デコード可能
- 再検索回避機能も維持
## テスト
tests/test_tools.py に新規テスト5件を追加:
- test_make_token_ensure_ascii_true
- test_search_and_fetch_token_excludes_body
- test_search_and_fetch_token_size_reduced
- test_search_and_fetch_continue_with_slim_token
- test_search_and_fetch_pagination_with_nonascii_title
## 検証結果
- pytest: 113 passed (新規5件含む、既存回帰なし)
- ruff check src/ tests/: All checks passed
- mypy src/: Success: no issues found in 30 source files
## ドキュメント
docs/tools-schema.md に実装ベースの差分を反映:
- make_token() の説明に ensure_ascii=True 化の旨を追記
- search_and_fetch 処理フローの注記に _slim_matched() による
body 除外とトークンサイズ削減の旨を追記
- トークンペイロード構造の matched 例から body を削除
|
|
はページング(継続トークン方式)に対応
mypy 6エラー修正
|