diff --git a/.env.example b/.env.example
index 979aab3..9aa4320 100644
--- a/.env.example
+++ b/.env.example
@@ -23,3 +23,5 @@ NEXT_PUBLIC_ONLYOFFICE_URL=http://localhost/office
NEXT_PUBLIC_HOCUSPOCUS_URL=ws://localhost/collab
# UltiAI (chemin proxy OpenWebUI — même origine)
NEXT_PUBLIC_AI_PUBLIC_PATH=/ai
+# Dev Next.js (:3000) : charger l'iframe depuis nginx (:80) pour cookies session + proxy OpenWebUI
+NEXT_PUBLIC_AI_ORIGIN=http://localhost
diff --git a/CLAUDE.md b/CLAUDE.md
index aeb0f25..51c9818 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -194,3 +194,21 @@ Cela permet de brancher n'importe quel service (Slack, Discord, n8n, Make, custo
### Docs internes
- `components/gmail/README.md` — arborescence composants
- `lib/stores/README.md` — architecture stores
+
+### Environnement local & agents
+
+Stack dev typique : **nginx (:80)** → `ultid` (Docker) + frontend Next.js (`pnpm dev`, proxy `/api/v1` via `ULTI_PROXY_ORIGIN`).
+
+**Les agents doivent redémarrer les services eux-mêmes** quand un changement l'exige — ne pas demander au développeur de le faire.
+
+| Changement | Action (depuis `ulti-backend/`) |
+|----------|----------------------------------|
+| Code Go (`internal/`, `cmd/`, migrations embarquées) | `./deploy/compose-up.sh up -d --build ultid` |
+| Variable `.env` lue au démarrage d'`ultid` | `./deploy/compose-up.sh up -d --build ultid` (régénère `.env.resolved`) |
+| Module Compose optionnel (OpenWebUI, Nextcloud, …) | `./deploy/compose-up.sh up -d` (overlay activé selon `.env`) |
+| Redémarrage simple sans rebuild | `./deploy/compose-up.sh restart ultid` |
+| Frontend Next.js (ce repo) | `pnpm dev` se recharge seul ; rebuild seulement si config Next/env change |
+
+Après restart backend, vérifier : `curl -s http://127.0.0.1:80/api/v1/ai/config` (JSON, pas 502).
+
+Repo backend : `../ulti-backend` — voir aussi `ulti-backend/CLAUDE.md`.
diff --git a/app/chat/page.tsx b/app/chat/page.tsx
index 96ad825..6153e1e 100644
--- a/app/chat/page.tsx
+++ b/app/chat/page.tsx
@@ -1,11 +1,13 @@
"use client"
+import Link from "next/link"
import { Sparkles } from "lucide-react"
import { AiChatIframe } from "@/components/ai/ai-chat-iframe"
import { useAiConfig, useAiQuota } from "@/lib/api/hooks/use-ai-queries"
+import { Button } from "@/components/ui/button"
export default function ChatPage() {
- const { data: config, isLoading } = useAiConfig()
+ const { data: config, isLoading, isError } = useAiConfig()
const { data: quota } = useAiQuota(Boolean(config?.enabled))
if (isLoading) {
@@ -16,17 +18,34 @@ export default function ChatPage() {
)
}
- if (!config?.enabled) {
+ if (isError) {
return (
-
+
- UltiAI n'est pas activé. Activez le plugin dans l'administration.
+ Impossible de contacter l'API UltiAI. Vérifiez que le backend est démarré.
)
}
+ if (!config?.enabled) {
+ return (
+
+
+
+ UltiAI n'est pas activé. Activez le plugin{" "}
+ UltiAI dans Administration → Plugins (puis enregistrez), ou définissez{" "}
+ AI_ASSISTANT_ENABLED=true dans le
+ déploiement backend.
+
Assistant IA
- Chat standalone et panneaux contextuels mail/drive/contacts.
+ Active le plugin UltiAI pour toute l'organisation. Le service OpenWebUI doit
+ aussi être déployé.
+ Activez le plugin UltiAI dans Administration → Plugins, ou définissez{" "}
+ AI_ASSISTANT_ENABLED=true dans le
+ déploiement, puis redémarrez le backend et OpenWebUI.
+
@@ -94,6 +201,148 @@ export function AiAssistantSection() {
+
+
+
+ Modèles autorisés
+
+ Liste vide = tous les modèles des fournisseurs LLM org. Sinon, seuls les modèles
+ autorisés sont visibles pour les utilisateurs. Le surnom remplace le nom technique.
+
+
+
+ {llm.providers.length === 0 ? (
+
+ Configurez d'abord un fournisseur LLM dans Administration → Fournisseurs LLM.
+
+ ) : (
+
+
+
+
+
+
+
+ )}
+
+ {discoverModels.isError ? (
+
+ {discoverModels.error instanceof Error
+ ? discoverModels.error.message
+ : "Impossible de lister les modèles sur ce fournisseur. Enregistrez d'abord le fournisseur LLM avec une clé API valide."}
+