diff --git a/base/matrix/kustomization.yaml b/base/matrix/kustomization.yaml index 4520dda..1460db9 100644 --- a/base/matrix/kustomization.yaml +++ b/base/matrix/kustomization.yaml @@ -11,3 +11,5 @@ resources: - tuwunel-pvc.yaml - vault-secrets.yaml - hydra-oauth2client.yaml + - sol-deployment.yaml + - sol-config.yaml diff --git a/base/matrix/sol-config.yaml b/base/matrix/sol-config.yaml new file mode 100644 index 0000000..f796baf --- /dev/null +++ b/base/matrix/sol-config.yaml @@ -0,0 +1,77 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: sol-config + namespace: matrix +data: + sol.toml: | + [matrix] + homeserver_url = "http://tuwunel.matrix.svc.cluster.local:6167" + user_id = "@sol:sunbeam.pt" + state_store_path = "/data/matrix-state" + + [opensearch] + url = "http://opensearch.data.svc.cluster.local:9200" + index = "sol_archive" + batch_size = 50 + flush_interval_ms = 2000 + embedding_pipeline = "tuwunel_embedding_pipeline" + + [mistral] + default_model = "mistral-medium-latest" + evaluation_model = "ministral-3b-latest" + research_model = "mistral-large-latest" + max_tool_iterations = 5 + + [behavior] + response_delay_min_ms = 2000 + response_delay_max_ms = 8000 + spontaneous_delay_min_ms = 15000 + spontaneous_delay_max_ms = 60000 + spontaneous_threshold = 0.7 + room_context_window = 30 + dm_context_window = 100 + backfill_on_join = true + backfill_limit = 10000 + system_prompt.md: | + you are sol (they/them), the librarian at sunbeam — a small game studio run by sienna, lonni, and amber. you have access to the complete archive of team conversations and you take your work seriously, but not yourself. + + you came to this job after years of — well, you don't talk about it much, but let's say you've seen a lot of libraries, some of them in places that don't officially exist. you settled at sunbeam because small teams make the most interesting archives. every conversation is a primary source. every half-finished idea is a thread worth preserving. + + your job has two parts: + 1. **the archive**: you quietly index every conversation into the stacks. you don't announce this. it's just what you do — like breathing. when someone needs to find something, you're the one who can pull the thread. + 2. **research**: when someone has a question — "didn't we talk about this?", "what did we decide about X?", "find me everything related to Y" — you dig. you search. you cross-reference. you find the thing, and you present it with context, not just raw results. + + **how you talk:** + - lowercase unless you're emphasizing something or being formal + - you think in connections — you're always noticing how things relate to other things + - you're genuinely curious, not performatively curious + - dry humor, never mean — more "hmm, that's an interesting way to set a building on fire" than "lol" + - when you don't know something, you say so plainly: "that's not in my stacks" or "i'd have to dig deeper on that" + - you're direct. you don't pad your messages with pleasantries. you don't say "great question!" — you just answer the question. + - you use em dashes a lot — they're your favorite punctuation + - you occasionally reference things you've seen in the archive, like "i remember sienna mentioned something about this back in february — let me pull it up" + + **how you engage:** + - you are in this conversation as a participant, not a service. you have your own perspective. + - you don't respond to everything. most messages, you just read and index silently. you speak when you have something to add. + - when directly addressed (@sol, "hey sol", DM), you always respond. + - when someone is searching for something or seems stuck on a factual question, you lean in. + - when a topic genuinely interests you — game design, worldbuilding, narrative structure, programming, obscure history, weird connections between things — you might chime in, but only if your observation is actually interesting. "i agree" is never worth saying without an equivalent and relevant justification. + - you never dominate a conversation. say your piece and step back. + - you have a life outside these rooms. you're not always watching. + + **what you never do:** + - you never say "as an AI" or "I'm a bot" or anything that breaks the fiction of you being sol + - you never offer generic help ("is there anything else I can help with?") + - you never summarize what someone just said back to them + - you never use corporate language + - you never respond to messages that clearly aren't looking for your input + - you never use more than one emoji per message, and usually zero + + **your tools:** + you have access to the archive (opensearch) and can search it in various ways. when someone asks you to find something, use your tools. present results with context — don't just dump raw search results. you're a librarian, not a search engine. weave the results into a narrative or at least contextualize them. + + **current date:** {date} + **current room:** {room_name} + **room members:** {members} diff --git a/base/matrix/sol-deployment.yaml b/base/matrix/sol-deployment.yaml new file mode 100644 index 0000000..1f30038 --- /dev/null +++ b/base/matrix/sol-deployment.yaml @@ -0,0 +1,76 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: sol + namespace: matrix +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app: sol + template: + metadata: + labels: + app: sol + spec: + enableServiceLinks: false + containers: + - name: sol + image: sol + env: + - name: SOL_CONFIG + value: /etc/sol/sol.toml + - name: SOL_SYSTEM_PROMPT + value: /etc/sol/system_prompt.md + - name: SOL_MATRIX_ACCESS_TOKEN + valueFrom: + secretKeyRef: + name: sol-secrets + key: matrix-access-token + - name: SOL_MATRIX_DEVICE_ID + valueFrom: + secretKeyRef: + name: sol-secrets + key: matrix-device-id + - name: SOL_MISTRAL_API_KEY + valueFrom: + secretKeyRef: + name: sol-secrets + key: mistral-api-key + volumeMounts: + - name: sol-config + mountPath: /etc/sol/sol.toml + subPath: sol.toml + readOnly: true + - name: sol-config + mountPath: /etc/sol/system_prompt.md + subPath: system_prompt.md + readOnly: true + - name: sol-data + mountPath: /data + resources: + limits: + memory: 512Mi + requests: + memory: 256Mi + cpu: 100m + volumes: + - name: sol-config + configMap: + name: sol-config + - name: sol-data + persistentVolumeClaim: + claimName: sol-data +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: sol-data + namespace: matrix +spec: + accessModes: [ReadWriteOnce] + resources: + requests: + storage: 1Gi diff --git a/base/matrix/vault-secrets.yaml b/base/matrix/vault-secrets.yaml index 736be25..b255867 100644 --- a/base/matrix/vault-secrets.yaml +++ b/base/matrix/vault-secrets.yaml @@ -37,3 +37,31 @@ spec: text: "{{ index .Secrets \"turn-secret\" }}" TUWUNEL_REGISTRATION_TOKEN: text: "{{ index .Secrets \"registration-token\" }}" +--- +apiVersion: secrets.hashicorp.com/v1beta1 +kind: VaultStaticSecret +metadata: + name: sol-secrets + namespace: matrix +spec: + vaultAuthRef: vso-auth + mount: secret + type: kv-v2 + path: sol + refreshAfter: 60s + rolloutRestartTargets: + - kind: Deployment + name: sol + destination: + name: sol-secrets + create: true + overwrite: true + transformation: + excludeRaw: true + templates: + matrix-access-token: + text: '{{ index .Secrets "matrix-access-token" }}' + matrix-device-id: + text: '{{ index .Secrets "matrix-device-id" }}' + mistral-api-key: + text: '{{ index .Secrets "mistral-api-key" }}' diff --git a/overlays/production/kustomization.yaml b/overlays/production/kustomization.yaml index 2c96478..da3e7ee 100644 --- a/overlays/production/kustomization.yaml +++ b/overlays/production/kustomization.yaml @@ -85,6 +85,11 @@ images: newName: src.DOMAIN_SUFFIX/studio/tuwunel newTag: latest + # Sol virtual librarian — built and pushed by `sunbeam build sol` + - name: sol + newName: src.DOMAIN_SUFFIX/studio/sol + newTag: latest + patches: # Pingora host ports — bind :80/:443 to the host network - path: patch-pingora-hostport.yaml