Add Ollama configuration for English coach
This commit is contained in:
2
.env.example
Normal file
2
.env.example
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
EXPO_PUBLIC_OLLAMA_BASE_URL=http://localhost:11434
|
||||||
|
EXPO_PUBLIC_OLLAMA_MODEL=llama3.2
|
||||||
24
README.md
24
README.md
@@ -78,6 +78,26 @@ Abhaengigkeiten installieren:
|
|||||||
npm install
|
npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Optionale lokale KI-Konfiguration anlegen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
In `.env` kannst du dein Ollama-Modell eintragen:
|
||||||
|
|
||||||
|
```text
|
||||||
|
EXPO_PUBLIC_OLLAMA_BASE_URL=http://localhost:11434
|
||||||
|
EXPO_PUBLIC_OLLAMA_MODEL=llama3.2
|
||||||
|
```
|
||||||
|
|
||||||
|
Wenn du die App auf einem echten Smartphone mit Expo Go testest, ist `localhost` das Smartphone selbst. Nutze dann die IP deines PCs, zum Beispiel:
|
||||||
|
|
||||||
|
```text
|
||||||
|
EXPO_PUBLIC_OLLAMA_BASE_URL=http://192.168.10.102:11434
|
||||||
|
EXPO_PUBLIC_OLLAMA_MODEL=qwen2.5:7b
|
||||||
|
```
|
||||||
|
|
||||||
App starten:
|
App starten:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -98,9 +118,9 @@ npm run web
|
|||||||
|
|
||||||
Hinweis: Fuer lokale iOS-Simulator-Builds wird macOS mit Xcode benoetigt. Mit Expo Go kann die App auf einem echten Geraet getestet werden.
|
Hinweis: Fuer lokale iOS-Simulator-Builds wird macOS mit Xcode benoetigt. Mit Expo Go kann die App auf einem echten Geraet getestet werden.
|
||||||
|
|
||||||
## Ollama Integration Geplant
|
## Ollama Integration
|
||||||
|
|
||||||
Der aktuelle Chat nutzt `src/services/mockAiCoach.ts`. Dieser Service ist die vorgesehene Stelle, um spaeter einen echten KI-Client zu integrieren.
|
Der Chat nutzt `src/services/ollamaClient.ts` fuer echte lokale KI-Antworten. Falls Ollama nicht erreichbar ist, faellt `src/services/mockAiCoach.ts` automatisch auf Beispielantworten zurueck. Die Modell-Konfiguration liegt in `src/config/ai.ts` und liest Werte aus `.env`.
|
||||||
|
|
||||||
Moeglicher lokaler Ablauf:
|
Moeglicher lokaler Ablauf:
|
||||||
|
|
||||||
|
|||||||
4
src/config/ai.ts
Normal file
4
src/config/ai.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export const aiConfig = {
|
||||||
|
ollamaBaseUrl: process.env.EXPO_PUBLIC_OLLAMA_BASE_URL ?? 'http://localhost:11434',
|
||||||
|
ollamaModel: process.env.EXPO_PUBLIC_OLLAMA_MODEL ?? 'llama3.2',
|
||||||
|
};
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
import type { EnglishLevel } from '../navigation/types';
|
import type { EnglishLevel } from '../navigation/types';
|
||||||
|
import { aiConfig } from '../config/ai';
|
||||||
|
import { askOllama } from './ollamaClient';
|
||||||
|
|
||||||
const examples: Record<EnglishLevel, string> = {
|
const examples: Record<EnglishLevel, string> = {
|
||||||
A1: 'Fast richtig! Correct: "I am learning English." Warum? Bei "I" nutzt du "am".',
|
A1: 'Fast richtig! Correct: "I am learning English." Warum? Bei "I" nutzt du "am".',
|
||||||
@@ -8,11 +10,15 @@ const examples: Record<EnglishLevel, string> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export async function getMockAiReply(input: string, level: EnglishLevel) {
|
export async function getMockAiReply(input: string, level: EnglishLevel) {
|
||||||
await new Promise((resolve) => setTimeout(resolve, 350));
|
|
||||||
|
|
||||||
if (!input.trim()) {
|
if (!input.trim()) {
|
||||||
return 'Schreib einen kurzen englischen Satz, dann korrigiere ich ihn fuer dein Level.';
|
return 'Schreib einen kurzen englischen Satz, dann korrigiere ich ihn fuer dein Level.';
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${examples[level]}\n\nDein Satz: "${input.trim()}"`;
|
try {
|
||||||
|
return await askOllama(input.trim(), level);
|
||||||
|
} catch {
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 350));
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${examples[level]}\n\nDein Satz: "${input.trim()}"\n\nKI Backend vorbereitet: ${aiConfig.ollamaModel}`;
|
||||||
}
|
}
|
||||||
|
|||||||
39
src/services/ollamaClient.ts
Normal file
39
src/services/ollamaClient.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { aiConfig } from '../config/ai';
|
||||||
|
import type { EnglishLevel } from '../navigation/types';
|
||||||
|
|
||||||
|
type OllamaGenerateResponse = {
|
||||||
|
response?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function askOllama(input: string, level: EnglishLevel) {
|
||||||
|
const response = await fetch(`${aiConfig.ollamaBaseUrl}/api/generate`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
model: aiConfig.ollamaModel,
|
||||||
|
stream: false,
|
||||||
|
prompt: buildPrompt(input, level),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Ollama request failed with ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = (await response.json()) as OllamaGenerateResponse;
|
||||||
|
return data.response?.trim() || 'Ich konnte keine Antwort erzeugen.';
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPrompt(input: string, level: EnglishLevel) {
|
||||||
|
return `Du bist ein freundlicher Englisch-Coach fuer deutsche Lernende.
|
||||||
|
Level: ${level}
|
||||||
|
Aufgabe:
|
||||||
|
1. Korrigiere den englischen Satz.
|
||||||
|
2. Erklaere den wichtigsten Fehler kurz auf Deutsch.
|
||||||
|
3. Gib eine bessere natuerliche Variante.
|
||||||
|
4. Antworte kurz und motivierend.
|
||||||
|
|
||||||
|
Satz des Nutzers: ${input}`;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user