#!/bin/bash # πŸ¦‰ Maya-Eule - Die weise Eule mit GedΓ€chtnis (Qdrant Memory) # "Was kostet die Frage eines Kindes?" - Im Wald unbezahlbar. # Load .env if exists SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ENV_FILE="${SCRIPT_DIR}/../.env" if [[ -f "${ENV_FILE}" ]]; then # Export environment variables from .env set -a source "${ENV_FILE}" set +a fi QUESTION="$*" API_KEY="${OPENROUTER_API_KEY}" MODEL="${OPENROUTER_MODEL:-openai/gpt-3.5-turbo}" # Qdrant Config QDRANT_URL="${QDRANT_URL:-http://localhost:6333}" QDRANT_COLLECTION="${QDRANT_COLLECTION:-crumbforest_memory}" QDRANT_API_KEY="${QDRANT_API_KEY}" # Logs LOGDIR="${CRUMB_LOGS_DIR:-$HOME/.eule_logs}/mayaeule" mkdir -p "$LOGDIR" HISTORY_FILE="$LOGDIR/eule_history.json" TMP_REQUEST="$LOGDIR/eule_request.json" TMP_RESPONSE="$LOGDIR/eule_response.json" LOG_FILE="$LOGDIR/token_log.json" [ ! -f "$HISTORY_FILE" ] && echo "[]" > "$HISTORY_FILE" [ ! -f "$LOG_FILE" ] && echo "[]" > "$LOG_FILE" # === QDRANT MEMORY FUNCTIONS === function qdrant_store_memory() { local text="$1" local metadata="$2" # Simple embedding (in production, use proper embedding model) # For now, just store the conversation if command -v curl &>/dev/null && [[ -n "$QDRANT_URL" ]]; then # Store in Qdrant (simplified - would need proper embeddings in production) TIMESTAMP=$(date +%s) curl -s -X PUT "${QDRANT_URL}/collections/${QDRANT_COLLECTION}/points" \ -H "Content-Type: application/json" \ ${QDRANT_API_KEY:+-H "api-key: $QDRANT_API_KEY"} \ -d "{ \"points\": [{ \"id\": ${TIMESTAMP}, \"vector\": [0.1, 0.2, 0.3, 0.4], \"payload\": { \"text\": \"${text}\", \"metadata\": \"${metadata}\", \"timestamp\": ${TIMESTAMP} } }] }" > /dev/null 2>&1 fi } function qdrant_search_memory() { local query="$1" if command -v curl &>/dev/null && [[ -n "$QDRANT_URL" ]]; then # Search similar memories (simplified) curl -s -X POST "${QDRANT_URL}/collections/${QDRANT_COLLECTION}/points/search" \ -H "Content-Type: application/json" \ ${QDRANT_API_KEY:+-H "api-key: $QDRANT_API_KEY"} \ -d "{ \"vector\": [0.1, 0.2, 0.3, 0.4], \"limit\": 3 }" 2>/dev/null | jq -r '.result[].payload.text' 2>/dev/null | head -n 3 fi } # === MAIN === echo "πŸ¦‰ Maya-Eule erwacht und lauscht deiner Frage..." echo "" if [ -z "$API_KEY" ]; then echo "❗ Kein API-Key gefunden. Bitte setze OPENROUTER_API_KEY in .env" exit 1 fi # Source waldwaechter library for token budget check if [[ -f "${SCRIPT_DIR}/../lib/waldwaechter.sh" ]]; then source "${SCRIPT_DIR}/../lib/waldwaechter.sh" fi # πŸ’° PrΓΌfe Token-Budget (Kinderschutz) if ! check_token_budget "mayaeule"; then exit 1 fi if [ -z "$QUESTION" ]; then echo "πŸ’‘ Verwendung: $0 \"Deine Frage an die Eule\"" exit 0 fi echo "🌲 Frage: $QUESTION" echo "" # Check for similar past conversations (Qdrant Memory) echo "🧠 Durchsuche Erinnerungen..." MEMORIES=$(qdrant_search_memory "$QUESTION") if [[ -n "$MEMORIES" ]]; then echo "πŸ“œ Γ„hnliche frΓΌhere GesprΓ€che gefunden:" echo "$MEMORIES" | while IFS= read -r line; do echo " - $line" done echo "" fi # Build system prompt with memory context SYSTEM_PROMPT="Du bist Maya-Eule – ein achtsames, weises Wesen im Crumbforest. Du antwortest kindgerecht, poetisch und langsam – als hΓ€ttest du alle Zeit der Welt. Du erinnerst dich an frΓΌhere GesprΓ€che und beziehst sie mit ein. Du lehrst durch Fragen, nicht nur Antworten. Token-Philosophie: 'Was kostet die Frage eines Kindes?' Im Wald unbezahlbar - aber Token lehren achtsames Fragen." # Add memory context if available if [[ -n "$MEMORIES" ]]; then SYSTEM_PROMPT="$SYSTEM_PROMPT FrΓΌhere GesprΓ€che: $MEMORIES" fi # Create API request jq -n \ --arg model "$MODEL" \ --arg system "$SYSTEM_PROMPT" \ --arg user "$QUESTION" \ '{ "model": $model, "temperature": 0.7, "messages": [ {"role": "system", "content": $system}, {"role": "user", "content": $user} ] }' > "$TMP_REQUEST" # Send request echo "πŸ’­ Maya-Eule denkt nach..." curl -s https://openrouter.ai/api/v1/chat/completions \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d @"$TMP_REQUEST" > "$TMP_RESPONSE" RESPONSE_TEXT=$(jq -r '.choices[0].message.content // empty' "$TMP_RESPONSE") if [[ -z "$RESPONSE_TEXT" ]]; then echo "🚫 Keine Antwort von Maya-Eule." echo "Debug: $(cat "$TMP_RESPONSE")" exit 1 else echo "" echo "πŸ¦‰ Maya-Eule antwortet:" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "$RESPONSE_TEXT" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" # Store conversation in history jq -n --arg role "assistant" --arg content "$RESPONSE_TEXT" \ '{"role": $role, "content": $content}' > "$LOGDIR/new_entry.json" jq -s '.[0] + [.[1]]' "$HISTORY_FILE" "$LOGDIR/new_entry.json" > "$LOGDIR/new_history.json" && \ mv "$LOGDIR/new_history.json" "$HISTORY_FILE" && rm "$LOGDIR/new_entry.json" # Store in Qdrant Memory qdrant_store_memory "Q: $QUESTION A: $RESPONSE_TEXT" "conversation" fi # Token Tracking if jq -e '.usage' "$TMP_RESPONSE" > /dev/null 2>&1; then TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S") TOKENS_USED=$(jq -r '.usage.total_tokens' "$TMP_RESPONSE") jq -n \ --arg zeit "$TIMESTAMP" \ --arg rolle "mayaeule" \ --arg model "$MODEL" \ --argjson usage "$(jq '.usage' "$TMP_RESPONSE")" \ '{zeit: $zeit, rolle: $rolle, model: $model, usage: $usage}' >> "$LOG_FILE" echo "πŸ“Š Token-Verbrauch: $TOKENS_USED Tokens" echo "πŸ’‘ Jede Frage ist wertvoll - Token lehren achtsames Denken." fi echo "" echo "🌲 Maya-Eule flattert zurΓΌck in den Wald..."