Skip to content

Commit 77db92e

Browse files
dbym4820claude
andcommitted
Add input source tracking to AI summaries
- Add input_source column to summaries table - Track what data source was used for summary generation: - pdf: PDF本文 - pdf_fetched: PDF本文(DOI経由で取得) - full_text: 本文テキスト - doi_fetch: DOIページから取得したテキスト - abstract: アブストラクトのみ - minimal: タイトル・メタデータのみ(推測を含む可能性あり) - Display input source label in frontend summary view - Show "ソース不明" for older summaries without source info 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent 5e3ed4c commit 77db92e

File tree

6 files changed

+101
-3
lines changed

6 files changed

+101
-3
lines changed

app/Http/Controllers/PaperController.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,8 @@ private function formatPaper(Paper $paper, bool $detailed = false): array
236236
'id' => $s->id,
237237
'ai_provider' => $s->ai_provider,
238238
'ai_model' => $s->ai_model,
239+
'input_source' => $s->input_source,
240+
'input_source_label' => $this->getInputSourceLabel($s->input_source),
239241
'summary_text' => $s->summary_text,
240242
'purpose' => $s->purpose,
241243
'methodology' => $s->methodology,
@@ -257,4 +259,20 @@ private function formatPaper(Paper $paper, bool $detailed = false): array
257259

258260
return $data;
259261
}
262+
263+
/**
264+
* 入力ソースの日本語ラベルを取得
265+
*/
266+
private function getInputSourceLabel(?string $source): string
267+
{
268+
return match ($source) {
269+
'pdf' => 'PDF本文',
270+
'pdf_fetched' => 'PDF本文(DOI経由で取得)',
271+
'full_text' => '本文テキスト',
272+
'doi_fetch' => 'DOIページから取得したテキスト',
273+
'abstract' => 'アブストラクトのみ',
274+
'minimal' => 'タイトル・メタデータのみ(※推測を含む可能性あり)',
275+
default => '不明',
276+
};
277+
}
260278
}

app/Http/Controllers/SummaryController.php

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ public function generate(Request $request): JsonResponse
8989
'paper_id' => $paper->id,
9090
'ai_provider' => $result['provider'],
9191
'ai_model' => $result['model'],
92+
'input_source' => $result['input_source'] ?? null,
9293
'summary_text' => $result['summary_text'],
9394
'purpose' => $result['purpose'] ?? null,
9495
'methodology' => $result['methodology'] ?? null,
@@ -106,6 +107,8 @@ public function generate(Request $request): JsonResponse
106107
'id' => $summary->id,
107108
'ai_provider' => $summary->ai_provider,
108109
'ai_model' => $summary->ai_model,
110+
'input_source' => $summary->input_source,
111+
'input_source_label' => $this->getInputSourceLabel($summary->input_source),
109112
'summary_text' => $summary->summary_text,
110113
'purpose' => $summary->purpose,
111114
'methodology' => $summary->methodology,
@@ -133,14 +136,17 @@ public function byPaper(Request $request, int $paperId): JsonResponse
133136
return response()->json(['error' => '論文が見つかりません'], 404);
134137
}
135138

139+
$self = $this;
136140
$summaries = Summary::where('paper_id', $paperId)
137141
->orderBy('created_at', 'desc')
138142
->get()
139-
->map(function ($s) {
143+
->map(function ($s) use ($self) {
140144
return [
141145
'id' => $s->id,
142146
'ai_provider' => $s->ai_provider,
143147
'ai_model' => $s->ai_model,
148+
'input_source' => $s->input_source,
149+
'input_source_label' => $self->getInputSourceLabel($s->input_source),
144150
'summary_text' => $s->summary_text,
145151
'purpose' => $s->purpose,
146152
'methodology' => $s->methodology,
@@ -153,4 +159,20 @@ public function byPaper(Request $request, int $paperId): JsonResponse
153159

154160
return response()->json($summaries);
155161
}
162+
163+
/**
164+
* 入力ソースの日本語ラベルを取得
165+
*/
166+
private function getInputSourceLabel(?string $source): string
167+
{
168+
return match ($source) {
169+
'pdf' => 'PDF本文',
170+
'pdf_fetched' => 'PDF本文(DOI経由で取得)',
171+
'full_text' => '本文テキスト',
172+
'doi_fetch' => 'DOIページから取得したテキスト',
173+
'abstract' => 'アブストラクトのみ',
174+
'minimal' => 'タイトル・メタデータのみ(※推測を含む可能性あり)',
175+
default => '不明',
176+
};
177+
}
156178
}

app/Models/Summary.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class Summary extends Model
1313
'paper_id',
1414
'ai_provider',
1515
'ai_model',
16+
'input_source', // 要約生成に使用したデータソース (pdf, full_text, doi_fetch, abstract, minimal)
1617
'summary_text',
1718
'purpose',
1819
'methodology',
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*/
12+
public function up(): void
13+
{
14+
Schema::table('summaries', function (Blueprint $table) {
15+
// 要約生成に使用したデータソース
16+
// 値: pdf, full_text, doi_fetch, abstract, minimal
17+
$table->string('input_source', 50)->nullable()->after('ai_model');
18+
});
19+
}
20+
21+
/**
22+
* Reverse the migrations.
23+
*/
24+
public function down(): void
25+
{
26+
Schema::table('summaries', function (Blueprint $table) {
27+
$table->dropColumn('input_source');
28+
});
29+
}
30+
};

resources/ts/components/PaperCard.tsx

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,7 @@ function PaperCardComponent({ paper, onTagsChange, hasAnyApiKey = true }: PaperC
801801
<Sparkles className="w-4 h-4 sm:w-5 sm:h-5 text-gray-600" />
802802
<span className="font-medium text-gray-900 text-sm sm:text-base">AI要約</span>
803803
</div>
804-
<div className="flex items-center gap-2">
804+
<div className="flex items-center gap-2 flex-wrap">
805805
<span className="text-xs px-2 py-1 bg-gray-100 rounded-full text-gray-600">
806806
{summary.ai_provider}
807807
</span>
@@ -810,6 +810,26 @@ function PaperCardComponent({ paper, onTagsChange, hasAnyApiKey = true }: PaperC
810810
{summary.ai_model}
811811
</span>
812812
)}
813+
{/* データソース表示 */}
814+
{summary.input_source_label && (
815+
<span
816+
className={`text-xs px-2 py-1 rounded-full ${
817+
summary.input_source === 'minimal'
818+
? 'bg-amber-100 text-amber-700'
819+
: summary.input_source === 'abstract'
820+
? 'bg-blue-100 text-blue-700'
821+
: 'bg-green-100 text-green-700'
822+
}`}
823+
title={`データソース: ${summary.input_source_label}`}
824+
>
825+
{summary.input_source_label}
826+
</span>
827+
)}
828+
{!summary.input_source_label && summary.input_source === null && (
829+
<span className="text-xs px-2 py-1 bg-gray-100 rounded-full text-gray-500">
830+
ソース不明
831+
</span>
832+
)}
813833
{/* 履歴ボタン(2件以上の履歴がある場合のみ表示) */}
814834
{allSummaries.length >= 2 && (
815835
<div className="relative" ref={historyDropdownRef}>
@@ -855,7 +875,7 @@ function PaperCardComponent({ paper, onTagsChange, hasAnyApiKey = true }: PaperC
855875
<span className="w-2 h-2 rounded-full border border-gray-300 flex-shrink-0" />
856876
)}
857877
<div className="flex-1 min-w-0">
858-
<div className="flex items-center gap-1.5">
878+
<div className="flex items-center gap-1.5 flex-wrap">
859879
<span className="text-xs font-medium text-gray-700">
860880
{s.ai_provider}
861881
</span>
@@ -865,6 +885,11 @@ function PaperCardComponent({ paper, onTagsChange, hasAnyApiKey = true }: PaperC
865885
</span>
866886
)}
867887
</div>
888+
{s.input_source_label && (
889+
<div className="text-[10px] text-gray-500 mt-0.5">
890+
{s.input_source_label}
891+
</div>
892+
)}
868893
<div className="text-[10px] text-gray-400 mt-0.5">
869894
{formatDateTime(s.created_at)}
870895
</div>

resources/ts/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ export interface Summary {
6969
paper_id: number;
7070
ai_provider: string;
7171
ai_model: string;
72+
input_source: string | null; // 要約生成に使用したデータソース
73+
input_source_label: string | null; // データソースの日本語ラベル
7274
summary_text: string;
7375
purpose: string | null;
7476
methodology: string | null;

0 commit comments

Comments
 (0)