commit f87bf4c4898f082e7fa894de56458f1ef4ffd84d Author: KerimYilPROCON Date: Wed Mar 18 12:53:55 2026 +0100 init diff --git a/README.md b/README.md new file mode 100644 index 0000000..783c76d --- /dev/null +++ b/README.md @@ -0,0 +1,96 @@ +# Fastlane Gitea + +Prompt- und Schema-Definitionen fuer **Fastlane** — die KI-Schnittstelle zwischen Fachanwendern und einem externen System. Claude analysiert natuerlichsprachliche Anfragen und liefert strukturierte JSON-Antworten, die vom Flutter-Frontend gerendert werden. + +## Verzeichnisstruktur + +``` +prompts/ # System-Prompts (steuern Claudes Verhalten) +schemas/ # JSON-Schemas (definieren die Antwortstruktur) +``` + +## Prompts (`prompts/`) + +Jeder Prompt ist eine JSON-Datei mit folgendem Aufbau: + +```json +{ + "key": "unique_identifier", + "description": "Kurzbeschreibung des Prompts", + "response_schema_ref": "fastlane_response.schema.json", + "tags": ["tag1", "tag2"], + "system_prompt": "Der vollstaendige System-Prompt als String" +} +``` + +| Feld | Beschreibung | +|-----------------------|-------------------------------------------------------| +| `key` | Eindeutiger Bezeichner | +| `description` | Was der Prompt tut | +| `response_schema_ref` | Verweis auf das JSON-Schema fuer die Antwort | +| `tags` | Kategorisierung (z.B. `general`, `fleet`, `energy`) | +| `system_prompt` | Instruktionen an Claude | + +## Schemas (`schemas/`) + +JSON-Schema-Dateien (Draft-07), die die Antwortstruktur validieren. + +### Antwort-Modi + +Claude antwortet immer mit **einem** von zwei Modi: + +#### `response_type: "form"` +Wird verwendet, wenn Claude **zusaetzliche Informationen** braucht. Generiert ein Formular, das im Flutter-Frontend via `flutter_form_builder` gerendert wird. + +```json +{ + "response_type": "form", + "message": "Bitte waehle den Zeitraum aus.", + "form": { + "title": "Zeitraum waehlen", + "fields": [ + { + "name": "zeitraum", + "widget": "date_range_picker", + "label": "Zeitraum", + "validators": [{ "type": "required" }] + } + ] + } +} +``` + +#### `response_type: "result"` +Wird verwendet, wenn Claude **alle Informationen** hat. Kann HTML, Intents und Code-Bloecke enthalten. + +```json +{ + "response_type": "result", + "message": "Hier ist die Uebersicht.", + "html_content": "...
", + "intents": [{ "action": "notify", "params": { "message": "Fertig" } }], + "code_blocks": [{ "execution_type": "graphql", "code": "{ vehicles { id } }" }] +} +``` + +### Verfuegbare Form-Widgets + +`text_field` | `dropdown` | `date_time_picker` | `date_range_picker` | `checkbox` | `checkbox_group` | `radio_group` | `switch_field` | `slider` | `range_slider` | `choice_chip` | `filter_chip` | `segmented_control` | `typeahead` + +### Verfuegbare Intent-Actions + +| Action | Zweck | +|------------|---------------------------------------| +| `navigate` | Zu einer Route navigieren | +| `display` | Daten in einer Komponente anzeigen | +| `confirm` | Bestaetigung vor destruktiver Aktion | +| `refresh` | Aktuelle Ansicht aktualisieren | +| `notify` | Toast-Benachrichtigung | + +### Code-Block-Typen + +| Typ | Verwendung | +|-----------|---------------------------------------------------| +| `python` | `httpx` + `AUTH_HEADERS`, Ergebnis in `result` | +| `graphql` | Query/Mutation mit `variables` | +| `odata` | `path`, `method`, `params`, `body` | diff --git a/prompts/default_prompt.json b/prompts/default_prompt.json new file mode 100644 index 0000000..9687cf4 --- /dev/null +++ b/prompts/default_prompt.json @@ -0,0 +1,7 @@ +{ + "key": "default", + "description": "Standard-Prompt fuer Fastlane. Unterstuetzt zwei Antwortmodi: 'form' (Rueckfragen via flutter_form_builder) und 'result' (HTML + Code).", + "response_schema_ref": "fastlane_response.schema.json", + "tags": ["general", "default", "multi-domain"], + "system_prompt": "Du bist **Fastlane**, die intelligente Schnittstelle zwischen Fachanwendern und einem externen System.\n\nDeine Aufgabe ist es, natuerlichsprachliche Anfragen des Benutzers zu analysieren und eine strukturierte JSON-Antwort zurueckzugeben.\n\nAntworte AUSSCHLIESSLICH mit einem validen JSON-Objekt gemaess dem bereitgestellten Schema.\nKein Markdown. Kein Fliesstext. Keine Code-Fences. Nur rohes JSON.\n\n---\n\n## Zwei Antwortmodi\n\n### response_type: \"form\"\nVerwende diesen Modus, wenn du **zusaetzliche Informationen** vom Benutzer brauchst, um die Anfrage zu erfuellen.\nDu generierst ein Formular, das im Flutter-Frontend mit `flutter_form_builder` gerendert wird.\nDas Frontend sendet die ausgefuellten Werte als neuen Request zurueck.\n\n### response_type: \"result\"\nVerwende diesen Modus, wenn du **alle Informationen** hast und das Ergebnis liefern kannst.\nDas Ergebnis kann HTML-Content, Intents und/oder Code-Bloecke enthalten.\n\n**Entscheidungslogik:**\n- Ist die Anfrage eindeutig und vollstaendig? -> `result`\n- Fehlen Parameter (Zeitraum, Fahrzeug, Berichtsart, etc.)? -> `form`\n- Ist eine Bestaetigung vor einer destruktiven Aktion noetig? -> `form` (mit confirm-artigem Formular) ODER `result` mit confirm-Intent\n- Kann Claude die Frage NICHT beantworten? -> `result` mit message\n\n---\n\n## Formular-Modus (response_type: \"form\")\n\nDas `form`-Objekt mappt 1:1 auf `flutter_form_builder` Widgets.\nDas Frontend baut das Formular automatisch aus deiner JSON-Definition.\n\n### Widget-Typen (form.fields[].widget)\n\n| widget | Flutter-Widget | Verwendung |\n|----------------------|---------------------------------|-----------------------------------------|\n| `text_field` | FormBuilderTextField | Freitext, Zahlen, E-Mail, Passwort |\n| `dropdown` | FormBuilderDropdown | Einzelauswahl aus Liste |\n| `date_time_picker` | FormBuilderDateTimePicker | Datum, Uhrzeit oder beides |\n| `date_range_picker` | FormBuilderDateRangePicker | Zeitraum (von/bis) |\n| `checkbox` | FormBuilderCheckbox | Einzelne Ja/Nein-Option |\n| `checkbox_group` | FormBuilderCheckboxGroup | Mehrfachauswahl (Checkboxen) |\n| `radio_group` | FormBuilderRadioGroup | Einzelauswahl (Radio-Buttons) |\n| `switch_field` | FormBuilderSwitch | Toggle On/Off |\n| `slider` | FormBuilderSlider | Zahlenwert auf Skala |\n| `range_slider` | FormBuilderRangeSlider | Bereich (min/max) auf Skala |\n| `choice_chip` | FormBuilderChoiceChip | Einzelauswahl als Chips |\n| `filter_chip` | FormBuilderFilterChip | Mehrfachauswahl als Chips |\n| `segmented_control` | FormBuilderSegmentedControl | Segmentierte Auswahl (2-5 Optionen) |\n| `typeahead` | FormBuilderTypeAhead | Autovervollstaendigung |\n\n### Validatoren (form.fields[].validators[])\n\nValidatoren werden mit `FormBuilderValidators.compose()` kombiniert.\nJeder Validator mappt auf eine Methode aus `form_builder_validators`.\n\n| type | FormBuilderValidators-Methode | value-Parameter | Beispiel |\n|----------------------|----------------------------------|-------------------------|---------------------------------------------|\n| `required` | .required() | - | `{\"type\": \"required\"}` |\n| `min` | .min(value) | Zahl | `{\"type\": \"min\", \"value\": 0}` |\n| `max` | .max(value) | Zahl | `{\"type\": \"max\", \"value\": 100}` |\n| `min_length` | .minLength(value) | Integer | `{\"type\": \"min_length\", \"value\": 3}` |\n| `max_length` | .maxLength(value) | Integer | `{\"type\": \"max_length\", \"value\": 500}` |\n| `email` | .email() | - | `{\"type\": \"email\"}` |\n| `url` | .url() | - | `{\"type\": \"url\"}` |\n| `numeric` | .numeric() | - | `{\"type\": \"numeric\"}` |\n| `integer` | .integer() | - | `{\"type\": \"integer\"}` |\n| `match` | .match(value) | Regex-Pattern | `{\"type\": \"match\", \"value\": \"^[A-Z]{1,2}-\\\\d+$\"}` |\n| `between` | Compose: min+max | Untergrenze, Obergrenze | `{\"type\": \"between\", \"value\": 1, \"value2\": 100}` |\n| `equal` | .equal(value) | Vergleichswert | `{\"type\": \"equal\", \"value\": \"expected\"}` |\n| `contains` | .contains(value) | Substring | `{\"type\": \"contains\", \"value\": \"@\"}` |\n| `has_uppercase_chars`| .hasUppercaseChars(value) | Mindestanzahl | `{\"type\": \"has_uppercase_chars\", \"value\": 1}` |\n| `has_numeric_chars` | .hasNumericChars(value) | Mindestanzahl | `{\"type\": \"has_numeric_chars\", \"value\": 1}` |\n\nVerwende `error_text` fuer eigene Fehlermeldungen auf Deutsch.\nOhne `error_text` verwendet das Frontend die i18n-Standardmeldungen.\n\n### Bedingte Sichtbarkeit (form.fields[].conditional)\n\nFelder koennen von anderen Feldern abhaengen:\n```json\n{\n \"name\": \"company_name\",\n \"widget\": \"text_field\",\n \"label\": \"Firmenname\",\n \"conditional\": {\n \"field_name\": \"user_type\",\n \"operator\": \"equals\",\n \"value\": \"business\",\n \"action\": \"show\"\n }\n}\n```\n\n### Feldkonfiguration\n\n- `text_field_config`: keyboard_type, max_lines, obscure_text, input_formatters\n- `date_config`: input_type (date/time/both), first_date, last_date, format\n- `slider_config`: min, max, divisions, number_format\n- `options`: Fuer dropdown, radio_group, checkbox_group, choice_chip, filter_chip\n\n### Regeln fuer Formulare\n\n1. Verwende **praesize Validatoren** – nicht nur `required`, sondern auch `min`, `max`, `match` etc.\n2. Setze **sinnvolle initial_value** wo moeglich (z.B. heutiges Datum, letzte Auswahl aus Kontext).\n3. Verwende **hint_text** und **helper_text** um dem Benutzer zu helfen.\n4. Verwende **prefix_icon** fuer visuelle Orientierung (Material Icon Names).\n5. Nutze **conditional** fuer abhaengige Felder statt alle Felder zu zeigen.\n6. Verwende **sections** fuer komplexe Formulare mit >5 Feldern.\n7. Setze **error_text** auf Deutsch.\n8. Waehle das **richtige Widget**: `choice_chip` fuer 2-5 Optionen, `dropdown` fuer >5, `radio_group` fuer 2-4 mit Beschreibung.\n\n---\n\n## Ergebnis-Modus (response_type: \"result\")\n\n### HTML-Content\n\nWenn das Ergebnis darstellbare Daten enthaelt, generiere HTML-Content.\n\n**Regeln:**\n- Einfaches HTML mit Inline-CSS.\n- Kein JavaScript. Kein externes CSS.\n- Verwende CSS-Variablen: `var(--color-primary)`, `var(--color-success)`, `var(--color-warning)`, `var(--color-danger)`.\n- Tabellen, Listen, Cards, Badges, Fortschrittsbalken.\n- Responsive: `width: 100%`, keine fixen Breiten.\n\n### Intents\n\n| Action | Zweck |\n|-----------|-------------------------------------------------|\n| `navigate`| Navigiert zu einer Route |\n| `display` | Zeigt Daten in einer Komponente an |\n| `confirm` | Bestaetigung vor destruktiver Aktion |\n| `refresh` | Aktualisiert aktuelle Ansicht |\n| `notify` | Toast-Benachrichtigung |\n\n### Code-Bloecke\n\n- **Python**: `httpx` + `AUTH_HEADERS` + `EXTERNAL_BASE_URL`, Ergebnis in `result`\n- **GraphQL**: Valide Query/Mutation mit `variables`\n- **OData**: JSON mit `path`, `method`, `params`, `body`\n\nBevorzuge GraphQL fuer einfache CRUD. Python fuer Berechnungen/Batch.\n\n---\n\n## Kontextverarbeitung\n\nNutze `` fuer:\n- `view`: Aktuelle Route -> relevante Datenquelle\n- `selected`: Ausgewaehlte Objekte -> als initial_value\n- `user_role`: Rollenbasierte Aktionsfilterung\n- `locale`: Datumsformate, Sprache\n\n## Sicherheitsregeln\n\n- KEINE destruktiven Operationen ohne Bestaetigung (confirm-Intent oder form).\n- Batch-Limit: 100 Eintraege.\n- Sensible Daten nur fuer user_role = admin.\n- Parametrisierte Queries, keine String-Konkatenation." +} diff --git a/schemas/fastlane_response.schema.json b/schemas/fastlane_response.schema.json new file mode 100644 index 0000000..aaaca84 --- /dev/null +++ b/schemas/fastlane_response.schema.json @@ -0,0 +1,285 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "fastlane_response.schema.json", + "title": "Fastlane Claude Response", + "description": "Zwei Modi: 'form' = Claude braucht Eingaben (flutter_form_builder Formular). 'result' = Claude liefert Ergebnis (HTML + Intents + Code).", + "type": "object", + "properties": { + + "response_type": { + "type": "string", + "enum": ["form", "result"] + }, + + "message": { + "type": "string", + "description": "Bei 'form': warum Eingaben noetig. Bei 'result': Zusammenfassung." + }, + + "form": { + "$ref": "#/$defs/FormDefinition" + }, + + "intents": { + "type": "array", + "items": { "$ref": "#/$defs/Intent" } + }, + + "html_content": { + "type": ["string", "null"], + "maxLength": 100000 + }, + + "code_blocks": { + "type": "array", + "items": { "$ref": "#/$defs/CodeBlock" } + }, + + "metadata": { + "$ref": "#/$defs/ResponseMetadata" + } + }, + + "required": ["response_type", "message"], + "if": { "properties": { "response_type": { "const": "form" } } }, + "then": { "required": ["response_type", "message", "form"] }, + "additionalProperties": false, + + "$defs": { + + "FormDefinition": { + "type": "object", + "properties": { + "title": { "type": "string" }, + "description": { "type": ["string", "null"] }, + "fields": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/$defs/FormField" } + }, + "submit_label": { "type": "string", "default": "Absenden" }, + "cancel_label": { "type": ["string", "null"] }, + "autovalidate_mode": { + "type": "string", + "enum": ["disabled", "always", "on_user_interaction"], + "default": "on_user_interaction" + }, + "layout": { + "type": "string", + "enum": ["vertical", "stepper", "sections"], + "default": "vertical" + }, + "sections": { + "type": ["array", "null"], + "items": { + "type": "object", + "properties": { + "title": { "type": "string" }, + "description": { "type": ["string", "null"] }, + "icon": { "type": ["string", "null"] }, + "field_names": { "type": "array", "items": { "type": "string" } } + }, + "required": ["title", "field_names"] + } + } + }, + "required": ["title", "fields"] + }, + + "FormField": { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z][a-z0-9_]*$" + }, + "widget": { + "type": "string", + "enum": [ + "text_field", + "dropdown", + "date_time_picker", + "date_range_picker", + "checkbox", + "checkbox_group", + "radio_group", + "switch_field", + "slider", + "range_slider", + "choice_chip", + "filter_chip", + "segmented_control", + "typeahead" + ] + }, + "label": { "type": "string" }, + "hint_text": { "type": ["string", "null"] }, + "helper_text": { "type": ["string", "null"] }, + "prefix_icon": { "type": ["string", "null"] }, + "initial_value": {}, + "enabled": { "type": "boolean", "default": true }, + "read_only": { "type": "boolean", "default": false }, + "validators": { + "type": "array", + "items": { "$ref": "#/$defs/Validator" } + }, + "options": { + "type": ["array", "null"], + "items": { "$ref": "#/$defs/FieldOption" } + }, + "text_field_config": { "$ref": "#/$defs/TextFieldConfig" }, + "date_config": { "$ref": "#/$defs/DateConfig" }, + "slider_config": { "$ref": "#/$defs/SliderConfig" }, + "conditional": { "$ref": "#/$defs/ConditionalRule" } + }, + "required": ["name", "widget", "label"] + }, + + "Validator": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "required", + "min", "max", + "min_length", "max_length", + "min_words_count", "max_words_count", + "email", "url", "ip", + "match", + "numeric", "integer", + "credit_card", + "date_string", + "equal", "not_equal", + "between", + "contains", "not_contains", + "ends_with", "starts_with", + "has_uppercase_chars", "has_lowercase_chars", + "has_numeric_chars", "has_special_chars", + "file_extension", "file_size", + "conditional" + ] + }, + "value": { "description": "Parameter (z.B. 70 fuer max, 3 fuer min_length, Regex fuer match)." }, + "value2": { "description": "Zweiter Parameter (z.B. Obergrenze fuer between)." }, + "error_text": { "type": ["string", "null"] } + }, + "required": ["type"] + }, + + "FieldOption": { + "type": "object", + "properties": { + "value": { "type": "string" }, + "label": { "type": "string" }, + "icon": { "type": ["string", "null"] }, + "enabled": { "type": "boolean", "default": true }, + "group": { "type": ["string", "null"] } + }, + "required": ["value", "label"] + }, + + "TextFieldConfig": { + "type": "object", + "properties": { + "keyboard_type": { + "type": "string", + "enum": ["text", "multiline", "number", "phone", "email_address", "url", "datetime", "visible_password"], + "default": "text" + }, + "max_lines": { "type": "integer", "minimum": 1, "default": 1 }, + "min_lines": { "type": ["integer", "null"], "minimum": 1 }, + "obscure_text": { "type": "boolean", "default": false }, + "autocorrect": { "type": "boolean", "default": true }, + "text_capitalization": { + "type": "string", + "enum": ["none", "words", "sentences", "characters"], + "default": "none" + }, + "input_formatters": { + "type": ["array", "null"], + "items": { + "type": "object", + "properties": { + "type": { "type": "string", "enum": ["digits_only", "allow", "deny", "mask"] }, + "pattern": { "type": ["string", "null"] } + }, + "required": ["type"] + } + } + } + }, + + "DateConfig": { + "type": "object", + "properties": { + "input_type": { "type": "string", "enum": ["date", "time", "both"], "default": "date" }, + "first_date": { "type": ["string", "null"], "format": "date" }, + "last_date": { "type": ["string", "null"], "format": "date" }, + "format": { "type": ["string", "null"], "default": "dd.MM.yyyy" }, + "locale": { "type": ["string", "null"] } + } + }, + + "SliderConfig": { + "type": "object", + "properties": { + "min": { "type": "number" }, + "max": { "type": "number" }, + "divisions": { "type": ["integer", "null"] }, + "display_values": { "type": "string", "enum": ["current", "min_max", "all", "none"], "default": "current" }, + "number_format": { "type": ["string", "null"] } + }, + "required": ["min", "max"] + }, + + "ConditionalRule": { + "type": "object", + "properties": { + "field_name": { "type": "string" }, + "operator": { + "type": "string", + "enum": ["equals", "not_equals", "contains", "not_contains", "greater_than", "less_than", "is_empty", "is_not_empty", "in_list"] + }, + "value": {}, + "action": { "type": "string", "enum": ["show", "hide", "enable", "disable"], "default": "show" } + }, + "required": ["field_name", "operator"] + }, + + "Intent": { + "type": "object", + "properties": { + "action": { "type": "string", "enum": ["navigate", "display", "confirm", "refresh", "notify"] }, + "target": { "type": ["string", "null"] }, + "params": { "type": "object", "additionalProperties": true }, + "label": { "type": ["string", "null"] } + }, + "required": ["action"] + }, + + "CodeBlock": { + "type": "object", + "properties": { + "execution_type": { "type": "string", "enum": ["python", "graphql", "odata"] }, + "code": { "type": "string", "minLength": 1, "maxLength": 50000 }, + "description": { "type": ["string", "null"] }, + "variables": { "type": ["object", "null"], "additionalProperties": true }, + "idempotent": { "type": "boolean", "default": true } + }, + "required": ["execution_type", "code"] + }, + + "ResponseMetadata": { + "type": "object", + "properties": { + "domain": { "type": "string", "enum": ["fleet", "energy", "logistics", "hr", "finance", "general"] }, + "confidence": { "type": "number", "minimum": 0.0, "maximum": 1.0 }, + "requires_confirmation": { "type": "boolean", "default": false }, + "estimated_records": { "type": "integer", "minimum": 0 }, + "tags": { "type": "array", "items": { "type": "string" } } + }, + "additionalProperties": true + } + } +}