@@ -7,88 +7,77 @@ const client = new PrismaClient();
77app . use ( express . json ( ) ) ;
88app . use ( express . static ( "./public" ) ) ;
99
10- const systemPrompt = `ユーザーの発話からタスクと時間を抽出してください。
11- 出力は必ず2行で、1行目がISO8601形式の日時(タイムゾーンは+09:00)、2行目がタスクタイトルです。
12- 時間情報がない場合は1行目を空にしてください。
13-
14- 例:
15- 入力: 明日の10時に会議
16- 出力:
17- 2024-01-21T10:00:00+09:00
18- 会議
19-
20- 入力: 買い物に行く
21- 出力:
22-
23- 買い物に行く` ;
24-
25- // 自然言語でタスクを追加(AI解析 + DB保存)
26- app . post ( "/todos/ai" , async ( request , response ) => {
27- try {
28- const result = await fetch (
29- "https://openrouter.ai/api/v1/chat/completions" ,
30- {
31- method : "POST" ,
32- headers : {
33- Authorization : `Bearer ${ process . env . OPENROUTER_API_KEY } ` ,
34- "Content-Type" : "application/json" ,
35- } ,
36- body : JSON . stringify ( {
37- model : "google/gemini-3-flash-preview" ,
38- messages : [
39- { role : "system" , content : systemPrompt } ,
40- { role : "user" , content : request . body . text } ,
41- ] ,
42- } ) ,
43- } ,
44- ) ;
45- const data = await result . json ( ) ;
46-
47- if ( ! data . choices || ! data . choices [ 0 ] ) {
48- response . status ( 500 ) . json ( { error : "AIからの応答が不正です" } ) ;
49- return ;
50- }
51-
52- const content = data . choices [ 0 ] . message . content ;
53- const lines = content . split ( "\n" ) ;
54- const dueAt = lines [ 0 ] ? new Date ( lines [ 0 ] ) : null ;
55- const title = lines [ 1 ] || "" ;
56-
57- const todo = await client . todo . create ( {
58- data : { title, due_at : dueAt } ,
59- } ) ;
60- response . json ( todo ) ;
61- } catch ( error ) {
62- console . error ( "Parse error:" , error ) ;
63- response . status ( 500 ) . json ( { error : "解析に失敗しました" } ) ;
64- }
65- } ) ;
66-
67- // タスク一覧を取得
6810app . get ( "/todos" , async ( request , response ) => {
69- const todos = await client . todo . findMany ( {
70- orderBy : { createdAt : "desc" } ,
71- } ) ;
11+ const todos = await client . todo . findMany ( { orderBy : { id : "asc" } } ) ;
7212 response . json ( todos ) ;
7313} ) ;
7414
75- // タスクを追加
7615app . post ( "/todos" , async ( request , response ) => {
16+ let dueAt = null ;
17+ if ( request . body . dueAt ) {
18+ dueAt = new Date ( request . body . dueAt ) ;
19+ }
7720 const todo = await client . todo . create ( {
7821 data : {
7922 title : request . body . title ,
80- due_at : request . body . due_at ? new Date ( request . body . due_at ) : null ,
23+ dueAt : dueAt ,
8124 } ,
8225 } ) ;
8326 response . json ( todo ) ;
8427} ) ;
8528
86- // タスクを削除
8729app . delete ( "/todos/:id" , async ( request , response ) => {
8830 await client . todo . delete ( {
8931 where : { id : parseInt ( request . params . id ) } ,
9032 } ) ;
9133 response . json ( { success : true } ) ;
9234} ) ;
9335
36+ app . post ( "/todos/ai" , async ( request , response ) => {
37+ const systemPrompt = `
38+ ユーザーの入力から、ToDoのタイトルと期限を抽出してください。
39+ 1行目にタイトル、2行目に期限(ISO8601形式、タイムゾーンは東京)を出力してください。
40+ 現在日時: ${ new Date ( ) . toISOString ( ) }
41+
42+ 例:
43+ 入力: 明日の10時に会議
44+ 出力:
45+ 会議
46+ 2024-01-21T10:00:00+09:00
47+
48+ 入力: 買い物に行く
49+ 出力:
50+ 買い物に行く
51+ ` ;
52+
53+ const result = await fetch ( "https://openrouter.ai/api/v1/chat/completions" , {
54+ method : "POST" ,
55+ headers : {
56+ Authorization : `Bearer ${ process . env . OPENROUTER_API_KEY } ` ,
57+ "Content-Type" : "application/json" ,
58+ } ,
59+ body : JSON . stringify ( {
60+ model : "openrouter/free" ,
61+ messages : [
62+ { role : "system" , content : systemPrompt } ,
63+ { role : "user" , content : request . body . instruction } ,
64+ ] ,
65+ } ) ,
66+ } ) ;
67+ const data = await result . json ( ) ;
68+ console . log ( data ) ;
69+ const content = data . choices [ 0 ] . message . content ;
70+ console . log ( content ) ;
71+ const lines = content . split ( "\n" ) ;
72+ const title = lines [ 0 ] ;
73+ let dueAt = null ;
74+ if ( lines [ 1 ] ) {
75+ dueAt = new Date ( lines [ 1 ] ) ;
76+ }
77+ const todo = await client . todo . create ( {
78+ data : { title : title , dueAt : dueAt } ,
79+ } ) ;
80+ response . json ( todo ) ;
81+ } ) ;
82+
9483app . listen ( 3000 ) ;
0 commit comments