Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion messages/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -7186,7 +7186,9 @@
"deleteDialog": {
"title": "Kategorie löschen",
"message": "Kategorie wirklich löschen?"
}
},
"aiFillPlaceholder": "z.B. «Reparatur-Tipps: Anleitungen zum Selberflicken, in Grün»",
"aiRefinePlaceholder": "z.B. «Beschreibung kürzer, Farbe eher blau»"
},
"dashboard": {
"myTasks": "Meine Aufgaben",
Expand Down Expand Up @@ -9135,5 +9137,15 @@
"body": "Diese Statuten wurden an der Gründungsversammlung beschlossen und sind seither in Kraft."
}
}
},
"aiForm": {
"fillTitle": "Mit KI ausfüllen",
"refineTitle": "Mit KI ändern",
"fillHint": "Beschreiben Sie es in eigenen Worten — das Formular füllt sich aus.",
"refineHint": "Sagen Sie, was geändert werden soll — z.B. «kürzer» oder «eher grün».",
"fillSubmit": "Felder ausfüllen",
"refineSubmit": "Änderung übernehmen",
"undo": "Rückgängig",
"changedFields": "{count, plural, one {# Feld geändert} other {# Felder geändert}}"
}
}
14 changes: 13 additions & 1 deletion messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -7102,7 +7102,9 @@
"deleteDialog": {
"title": "Delete category",
"message": "Really delete this category?"
}
},
"aiFillPlaceholder": "e.g. «Repair tips: guides for fixing things yourself, in green»",
"aiRefinePlaceholder": "e.g. «shorter description, make it blue»"
},
"dashboard": {
"myTasks": "My tasks",
Expand Down Expand Up @@ -9118,5 +9120,15 @@
"body": "These statutes were adopted at the founding assembly and have been in force since."
}
}
},
"aiForm": {
"fillTitle": "Fill with AI",
"refineTitle": "Change with AI",
"fillHint": "Describe it in your own words and the form fills in.",
"refineHint": "Say what to change — e.g. «shorter» or «make it green».",
"fillSubmit": "Fill fields",
"refineSubmit": "Apply change",
"undo": "Undo",
"changedFields": "{count, plural, one {# field changed} other {# fields changed}}"
}
}
14 changes: 13 additions & 1 deletion messages/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -7053,7 +7053,9 @@
"deleteDialog": {
"title": "Eliminar categoría",
"message": "¿Seguro que desea eliminar la categoría?"
}
},
"aiFillPlaceholder": "p.ej. «Consejos de reparación: guías para arreglar tú mismo, en verde»",
"aiRefinePlaceholder": "p.ej. «descripción más corta, mejor azul»"
},
"dashboard": {
"myTasks": "Mis tareas",
Expand Down Expand Up @@ -9013,5 +9015,15 @@
"body": "Estos estatutos fueron aprobados en la asamblea constitutiva y están en vigor desde entonces."
}
}
},
"aiForm": {
"fillTitle": "Rellenar con IA",
"refineTitle": "Modificar con IA",
"fillHint": "Descríbelo con tus palabras y el formulario se rellena.",
"refineHint": "Di qué cambiar — p.ej. «más corto» o «mejor en verde».",
"fillSubmit": "Rellenar campos",
"refineSubmit": "Aplicar cambio",
"undo": "Deshacer",
"changedFields": "{count, plural, one {# campo modificado} other {# campos modificados}}"
}
}
14 changes: 13 additions & 1 deletion messages/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -7053,7 +7053,9 @@
"deleteDialog": {
"title": "Supprimer la catégorie",
"message": "Voulez-vous vraiment supprimer la catégorie ?"
}
},
"aiFillPlaceholder": "p.ex. «Conseils de réparation : guides pour réparer soi-même, en vert»",
"aiRefinePlaceholder": "p.ex. «description plus courte, plutôt bleu»"
},
"dashboard": {
"myTasks": "Mes tâches",
Expand Down Expand Up @@ -9015,5 +9017,15 @@
"body": "Les présents statuts ont été adoptés lors de l'assemblée constitutive et sont en vigueur depuis lors."
}
}
},
"aiForm": {
"fillTitle": "Remplir avec l'IA",
"refineTitle": "Modifier avec l'IA",
"fillHint": "Décrivez-le avec vos mots — le formulaire se remplit.",
"refineHint": "Dites ce qu'il faut changer — p.ex. «plus court» ou «plutôt vert».",
"fillSubmit": "Remplir les champs",
"refineSubmit": "Appliquer la modification",
"undo": "Annuler",
"changedFields": "{count, plural, one {# champ modifié} other {# champs modifiés}}"
}
}
14 changes: 13 additions & 1 deletion messages/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -7053,7 +7053,9 @@
"deleteDialog": {
"title": "Elimina categoria",
"message": "Eliminare davvero la categoria?"
}
},
"aiFillPlaceholder": "p.es. «Consigli di riparazione: guide per riparare da sé, in verde»",
"aiRefinePlaceholder": "p.es. «descrizione più corta, piuttosto blu»"
},
"dashboard": {
"myTasks": "I miei compiti",
Expand Down Expand Up @@ -9013,5 +9015,15 @@
"body": "Il presente statuto è stato approvato dall'assemblea costitutiva ed è da allora in vigore."
}
}
},
"aiForm": {
"fillTitle": "Compila con l'IA",
"refineTitle": "Modifica con l'IA",
"fillHint": "Descrivilo con parole tue — il modulo si compila.",
"refineHint": "Di' cosa cambiare — p.es. «più corto» o «piuttosto verde».",
"fillSubmit": "Compila i campi",
"refineSubmit": "Applica la modifica",
"undo": "Annulla",
"changedFields": "{count, plural, one {# campo modificato} other {# campi modificati}}"
}
}
14 changes: 13 additions & 1 deletion messages/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -7053,7 +7053,9 @@
"deleteDialog": {
"title": "カテゴリーを削除",
"message": "本当にこのカテゴリーを削除しますか?"
}
},
"aiFillPlaceholder": "例:「修理のヒント:自分で直すためのガイド、緑で」",
"aiRefinePlaceholder": "例:「説明を短く、青にして」"
},
"dashboard": {
"myTasks": "マイタスク",
Expand Down Expand Up @@ -9013,5 +9015,15 @@
"body": "本定款は設立総会において議決され、以後効力を有する。"
}
}
},
"aiForm": {
"fillTitle": "AIで入力",
"refineTitle": "AIで変更",
"fillHint": "自分の言葉で説明すると、フォームが入力されます。",
"refineHint": "変更内容を伝えてください — 例:「短く」「緑に」。",
"fillSubmit": "項目を入力",
"refineSubmit": "変更を適用",
"undo": "元に戻す",
"changedFields": "{count, plural, other {# 件の項目を変更しました}}"
}
}
14 changes: 13 additions & 1 deletion messages/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -7053,7 +7053,9 @@
"deleteDialog": {
"title": "카테고리 삭제",
"message": "정말 카테고리를 삭제하시겠습니까?"
}
},
"aiFillPlaceholder": "예: 「수리 팁: 직접 고치는 가이드, 초록색으로」",
"aiRefinePlaceholder": "예: 「설명을 더 짧게, 파란색으로」"
},
"dashboard": {
"myTasks": "내 업무",
Expand Down Expand Up @@ -9013,5 +9015,15 @@
"body": "본 정관은 창립총회에서 의결되었으며 그 이후 효력을 가진다."
}
}
},
"aiForm": {
"fillTitle": "AI로 채우기",
"refineTitle": "AI로 수정",
"fillHint": "직접 설명하면 양식이 채워집니다.",
"refineHint": "무엇을 바꿀지 말해 주세요 — 예: 「더 짧게」, 「초록색으로」.",
"fillSubmit": "항목 채우기",
"refineSubmit": "변경 적용",
"undo": "실행 취소",
"changedFields": "{count, plural, other {# 개 항목이 변경되었습니다}}"
}
}
14 changes: 13 additions & 1 deletion messages/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -7047,7 +7047,9 @@
"deleteDialog": {
"title": "Удалить категорию",
"message": "Действительно удалить категорию?"
}
},
"aiFillPlaceholder": "напр. «Советы по ремонту: инструкции для самостоятельного ремонта, зелёным»",
"aiRefinePlaceholder": "напр. «описание короче, лучше синий»"
},
"dashboard": {
"myTasks": "Мои задачи",
Expand Down Expand Up @@ -9007,5 +9009,15 @@
"body": "Настоящий устав был принят на учредительном собрании и с тех пор действует."
}
}
},
"aiForm": {
"fillTitle": "Заполнить с ИИ",
"refineTitle": "Изменить с ИИ",
"fillHint": "Опишите своими словами — форма заполнится.",
"refineHint": "Скажите, что изменить — напр. «короче» или «сделать зелёным».",
"fillSubmit": "Заполнить поля",
"refineSubmit": "Применить изменение",
"undo": "Отменить",
"changedFields": "{count, plural, one {# поле изменено} few {# поля изменено} other {# полей изменено}}"
}
}
14 changes: 14 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
"@aws-sdk/client-s3": "^3.1070.0",
"@eslint/eslintrc": "^3.3.3",
"@eslint/js": "^9.39.2",
"@fleet/ai-forms": "github:maonakamoto/ai-forms#v0.1.0",
"@sentry/nextjs": "^10.61.0",
"@tiptap/extension-link": "^3.29.2",
"@tiptap/extension-table": "^3.29.2",
Expand Down
51 changes: 51 additions & 0 deletions src/app/api/ai/form-assist/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* Form Assist API
*
* POST /api/ai/form-assist
*
* One route for every AI-assisted form in the app. Adding assistance to a new
* form means adding it to AI_FORMS — nothing here changes.
*
* The field registry stays server-side on purpose: the client names a form,
* never the fields, so it cannot widen what the model is allowed to write.
*
* Auth required — staff only, same as the other advisors.
*/

import { createFormAssistHandler } from '@fleet/ai-forms/server'
import { auth } from '@/auth'
import { callWithFallback } from '@/lib/ai/providers'
import { AI_FORMS } from '@/config/ai-forms'

export const POST = createFormAssistHandler({
targets: AI_FORMS,

authorize: async () => {
const session = await auth()
return session?.user?.email
? { ok: true }
: { ok: false, status: 401, error: 'Nicht angemeldet.' }
},

// Reuses the app's own provider chain, so form assistance inherits the
// existing key handling, fallback order and timeouts rather than adding a
// second policy to keep in sync.
complete: async ({ system, prompt, maxTokens, temperature }) => {
const result = await callWithFallback({
systemPrompt: system,
userPrompt: prompt,
maxTokens,
temperature,
})
if (!result) {
throw new Error('Kein KI-Anbieter verfügbar.')
}
return result.text
},

// Deliberately NOT wrapped in the house { success, data } envelope. This
// route has exactly one client — useAiForm — and the hook reads the
// AssistResult shape directly (`result.ok`, `result.values`). Wrapping it
// typechecks fine and breaks at runtime, because the hook would see an
// object with no `ok` and report every successful call as a failure.
})
Loading
Loading