Commit 9406624575be

Vincent Demeester <vincent@sbr.pm>
2025-07-07 15:52:37
Make aichat configuration dynamic…
As I store API_KEYs in passage. Signed-off-by: Vincent Demeester <vincent@sbr.pm>
1 parent 7425724
Changed files (3)
home/common/dev/ai.nix
@@ -1,21 +1,5 @@
-{ pkgs, ... }:
+{ lib, pkgs, ... }:
 {
-  programs.aichat = {
-    # enable = true;
-    settings = {
-      model = "gemini:gemini-2.5-flash-preview-04-17";
-      wrap = 150;
-      save_session = true;
-      clients = [
-        {
-          type = "gemini";
-          name = "gemini";
-          api_base = "";
-          # api_key = "passage:ai/gemini/api_key";
-        }
-      ];
-    };
-  };
   home.packages = with pkgs; [
     aichat
     aider-chat
@@ -33,4 +17,16 @@
     github-mcp-server
     amp
   ];
+
+  xdg.configFile."aichat/config.yaml.in".source = ./aichat.yaml;
+  xdg.configFile."aichat/update-config" = {
+    source = ./aichat-update-config;
+    executable = true;
+  };
+  home.activation = {
+    # linkGeneration writeBoundary
+    aichat-configuration = lib.hm.dag.entryAfter [ "linkGeneration" ] ''
+      /home/vincent/.config/aichat/update-config
+    '';
+  };
 }
home/common/dev/aichat-update-config
@@ -0,0 +1,71 @@
+#!/usr/bin/env bash
+# Script to update the aichat configuration from a .yaml.in
+export PATH=$PATH:/run/current-system/sw/bin
+export PASSAGE_DIR=/home/vincent/.local/share/passage
+export PASSAGE_IDENTITIES_FILE=/home/vincent/.local/share/passage/identities
+
+# Define the input and output file names
+INPUT_FILE="${HOME}/.config/aichat/config.yaml.in"
+OUTPUT_FILE="${HOME}/.config/aichat/config.yaml"
+
+# Check if the input file exists
+if [ ! -f "$INPUT_FILE" ]; then
+	echo "Error: Input file '$INPUT_FILE' not found."
+	exit 1
+fi
+
+# Create an empty output file
+>"$OUTPUT_FILE"
+
+# Read the input file line by line
+while IFS= read -r line; do
+	# Regex to find "passage::" followed by characters that are NOT a double quote or whitespace,
+	# until a double quote or whitespace is encountered.
+	# This specifically targets the content *inside* the quotes if the passage:: is quoted.
+	if [[ "$line" =~ passage::([^\"]+) ]]; then # Changed regex to capture everything after passage:: until a quote
+		# BASH_REMATCH[0] will be the entire matched string, e.g., "passage::ai/gemini/api_key"
+		# BASH_REMATCH[1] will be the captured group, e.g., "ai/gemini/api_key"
+
+		# The full string that needs to be replaced in the line (including "passage::")
+		passage_full_string="passage::${BASH_REMATCH[1]}"
+
+		# The string to pass to the 'passage show' command (without "passage::" or quotes)
+		# We need to trim any potential trailing quote from BASH_REMATCH[1] if the regex was greedy.
+		passage_string_for_command=$(echo "${BASH_REMATCH[1]}" | sed 's/"$//' | xargs)
+
+		echo "Found passage key in line: $passage_full_string"
+		echo "Executing command: passage show \"$passage_string_for_command\"" # Echoing with quotes for clarity
+
+		# Execute the passage command and capture its output
+		# Trim newlines and other whitespace from the actual command output
+		passage_output=$(passage show "$passage_string_for_command" 2>/dev/null | tr -d '\n\r' | xargs)
+
+		# Check if the passage command was successful and returned output
+		if [ $? -eq 0 ] && [ -n "$passage_output" ]; then
+			# Escape special characters in the actual output for sed
+			escaped_passage_output=$(printf %s "$passage_output" | sed -e 's/[\/&]/\\&/g')
+
+			# Escape special characters in the passage_full_string for sed pattern
+			escaped_passage_full_string=$(printf %s "$passage_full_string" | sed -e 's/[\/&]/\\&/g')
+
+			# Replace the original passage string with the command output.
+			# We are replacing the "passage::value" with the actual "value"
+			# It's crucial that the replacement happens within the existing quotes in the YAML.
+			# So, if line is `api_key: "passage::val"`, we want `api_key: "ACTUAL_VAL"`
+			# The sed should replace `passage::val` with `ACTUAL_VAL`.
+			modified_line=$(echo "$line" | sed "s/${escaped_passage_full_string}/${escaped_passage_output}/")
+
+			echo "Modified line: $modified_line"
+			echo "$modified_line" >>"$OUTPUT_FILE"
+		else
+			echo "Warning: 'passage show $passage_string_for_command' failed or returned empty. Keeping original line."
+			echo "$line" >>"$OUTPUT_FILE"
+		fi
+	else
+		# If no "passage::" found, write the original line
+		echo "$line" >>"$OUTPUT_FILE"
+	fi
+done <"$INPUT_FILE"
+
+echo "Processing complete. Modified content written to '$OUTPUT_FILE'."
+echo "You can now inspect '$OUTPUT_FILE' and if satisfied, replace '$INPUT_FILE' with it."
home/common/dev/aichat.yaml
@@ -0,0 +1,51 @@
+model: gemini:gemini-2.5-flash-preview-04-17
+wrap: 150
+save_session: true
+clients:
+  - type: gemini
+    name: gemini
+    api_base: https://generativelanguage.googleapis.com/v1beta
+    api_key: "passage::ai/gemini/api_key"
+    patch:
+      chat_completions:
+        ".*":
+          body:
+            safetySettings:
+              - category: HARM_CATEGORY_HARASSMENT
+                threshold: BLOCK_NONE
+              - category: HARM_CATEGORY_HATE_SPEECH
+                threshold: BLOCK_NONE
+              - category: HARM_CATEGORY_SEXUALLY_EXPLICIT
+                threshold: BLOCK_NONE
+              - category: HARM_CATEGORY_DANGEROUS_CONTENT
+                threshold: BLOCK_NONE
+
+  - type: openai-compatible
+    name: ollama
+    api_base: http://127.0.0.1:11434/v1
+    api_key: null
+
+  - type: openai-compatible
+    name: groq
+    api_base: https://api.groq.com/openai/v1
+    api_key: "passage::ai/groq/api_key"
+
+  # See https://platform.deepseek.com/api-docs/
+  - type: openai-compatible
+    name: deepseek
+    api_base: https://api.deepseek.com
+    api_key: "passage::ai/deepseek/api_key"
+
+  - type: openai-compatible
+    name: openrouter
+    api_base: https://openrouter.ai/api/v1
+    api_key: "passage::ai/openroute/api_key"
+
+  # - type: openai-compatible
+  #   name: redhat-maas-deepseek
+  #   api_base: https://deepseek-r1-distill-qwen-14b-maas-apicast-production.apps.prod.rhoai.rh-aiservices-bu.com/v1
+  #   api_key: "passage::ai/gemini/api_key"
+  #   models:
+  #     - name: deepseek-r1-distill-qwen-14b
+  #       description: DeepSeek R1 Distill Qwen 14B
+