mirror of
https://github.com/dredx/prole.git
synced 2026-09-24 15:04:31 +00:00
Compare commits
6 Commits
d114801758
...
f0d3a85fbc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0d3a85fbc | ||
|
|
e5fe9dc2a1 | ||
|
|
3b1014ee4d | ||
|
|
5f60ede5d7 | ||
|
|
5aacee46d8 | ||
|
|
23cf4a8585 |
@ -82,6 +82,9 @@ zinfandel.prole.org ansible_user=chrisfu ansible_ssh_private_key_file=~/.ssh/id_
|
|||||||
morgoth.prole.org ansible_user=chrisfu ansible_connection=winrm ansible_winrm_transport=ntlm ansible_winrm_server_cert_validation=ignore
|
morgoth.prole.org ansible_user=chrisfu ansible_connection=winrm ansible_winrm_transport=ntlm ansible_winrm_server_cert_validation=ignore
|
||||||
fairyland.prole.org ansible_user=chrisfu ansible_connection=winrm ansible_winrm_transport=ntlm ansible_winrm_server_cert_validation=ignore
|
fairyland.prole.org ansible_user=chrisfu ansible_connection=winrm ansible_winrm_transport=ntlm ansible_winrm_server_cert_validation=ignore
|
||||||
|
|
||||||
|
[workstations_windows:vars]
|
||||||
|
ansible_become=false
|
||||||
|
ansible_become_method=runas
|
||||||
|
|
||||||
[workstations:children]
|
[workstations:children]
|
||||||
workstations_windows
|
workstations_windows
|
||||||
|
|
||||||
|
|||||||
165
infrastructure/playbooks/windows_ollama_models.yml
Normal file
165
infrastructure/playbooks/windows_ollama_models.yml
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
---
|
||||||
|
# windows_ollama_models.yml — Manage local Ollama model variants on Windows GPU hosts.
|
||||||
|
#
|
||||||
|
# This is the small, Ansible-like control surface for the two Windows NVIDIA
|
||||||
|
# machines that run Ollama for Prole embedding workloads.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# cd infrastructure
|
||||||
|
# ansible-playbook playbooks/windows_ollama_models.yml --ask-pass
|
||||||
|
#
|
||||||
|
# Single host:
|
||||||
|
# ansible-playbook playbooks/windows_ollama_models.yml --limit morgoth.prole.org --ask-pass
|
||||||
|
#
|
||||||
|
# Dry-run intent only:
|
||||||
|
# ansible-playbook playbooks/windows_ollama_models.yml --check --diff --ask-pass
|
||||||
|
#
|
||||||
|
# Inventory target:
|
||||||
|
# [workstations_windows]
|
||||||
|
# morgoth.prole.org
|
||||||
|
# fairyland.prole.org
|
||||||
|
#
|
||||||
|
# What this deploys:
|
||||||
|
# ollama create mxbai-embed-large-2k FROM mxbai-embed-large PARAMETER num_ctx 2048
|
||||||
|
#
|
||||||
|
# The desired Modelfile is written to C:\ProgramData\Prole\Ollama\Models and a
|
||||||
|
# SHA-256 marker is recorded after successful creation. Re-running the playbook
|
||||||
|
# is safe: it recreates the model only when the target model is missing or the
|
||||||
|
# managed Modelfile content changes.
|
||||||
|
|
||||||
|
- name: Configure Prole Ollama models on Windows GPU hosts
|
||||||
|
hosts: workstations_windows
|
||||||
|
gather_facts: false
|
||||||
|
become: false
|
||||||
|
|
||||||
|
vars:
|
||||||
|
ansible_become: false
|
||||||
|
ansible_become_method: runas
|
||||||
|
prole_ollama_models_dir: 'C:\ProgramData\Prole\Ollama\Models'
|
||||||
|
prole_ollama_models:
|
||||||
|
- name: mxbai-embed-large-2k
|
||||||
|
base: mxbai-embed-large
|
||||||
|
parameters:
|
||||||
|
num_ctx: 2048
|
||||||
|
|
||||||
|
# Override per-host if WinRM cannot see ollama.exe in PATH.
|
||||||
|
# Common installs include:
|
||||||
|
# C:\Program Files\Ollama\ollama.exe
|
||||||
|
# C:\Users\<user>\AppData\Local\Programs\Ollama\ollama.exe
|
||||||
|
ollama_windows_exe: ''
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Resolve ollama.exe path
|
||||||
|
ansible.windows.win_shell: |
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$candidates = @()
|
||||||
|
|
||||||
|
if ('{{ ollama_windows_exe }}') {
|
||||||
|
$candidates += '{{ ollama_windows_exe }}'
|
||||||
|
}
|
||||||
|
|
||||||
|
$cmd = Get-Command ollama.exe -ErrorAction SilentlyContinue
|
||||||
|
if ($cmd) {
|
||||||
|
$candidates += $cmd.Source
|
||||||
|
}
|
||||||
|
|
||||||
|
$candidates += @(
|
||||||
|
'C:\Program Files\Ollama\ollama.exe',
|
||||||
|
'C:\Users\{{ ansible_user }}\AppData\Local\Programs\Ollama\ollama.exe'
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach ($candidate in $candidates | Select-Object -Unique) {
|
||||||
|
if ($candidate -and (Test-Path -LiteralPath $candidate)) {
|
||||||
|
Write-Output $candidate
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw 'ollama.exe was not found. Set ollama_windows_exe for this host or install Ollama first.'
|
||||||
|
args:
|
||||||
|
executable: powershell.exe
|
||||||
|
register: ollama_exe_result
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Set resolved Ollama executable fact
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
ollama_resolved_exe: "{{ ollama_exe_result.stdout_lines[0] }}"
|
||||||
|
|
||||||
|
- name: Ensure Prole Ollama model state directory exists
|
||||||
|
ansible.windows.win_file:
|
||||||
|
path: "{{ prole_ollama_models_dir }}"
|
||||||
|
state: directory
|
||||||
|
|
||||||
|
- name: Show planned Ollama model configuration in check mode
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: >-
|
||||||
|
Would ensure {{ item.name }} exists from {{ item.base }} with num_ctx={{ item.parameters.num_ctx }}
|
||||||
|
using {{ ollama_resolved_exe }}.
|
||||||
|
loop: "{{ prole_ollama_models }}"
|
||||||
|
when: ansible_check_mode
|
||||||
|
|
||||||
|
- name: Create or update managed Ollama model variants
|
||||||
|
ansible.windows.win_shell: |
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$ollama = '{{ ollama_resolved_exe }}'
|
||||||
|
$modelName = '{{ item.name }}'
|
||||||
|
$baseModel = '{{ item.base }}'
|
||||||
|
$modelDir = '{{ prole_ollama_models_dir }}'
|
||||||
|
$modelfilePath = Join-Path $modelDir "$modelName.Modelfile"
|
||||||
|
$markerPath = Join-Path $modelDir "$modelName.Modelfile.sha256"
|
||||||
|
|
||||||
|
$modelfile = @"
|
||||||
|
FROM {{ item.base }}
|
||||||
|
PARAMETER num_ctx {{ item.parameters.num_ctx }}
|
||||||
|
"@.TrimStart()
|
||||||
|
|
||||||
|
Set-Content -LiteralPath $modelfilePath -Value $modelfile -Encoding ascii -NoNewline
|
||||||
|
$desiredHash = (Get-FileHash -Algorithm SHA256 -LiteralPath $modelfilePath).Hash
|
||||||
|
$recordedHash = if (Test-Path -LiteralPath $markerPath) {
|
||||||
|
(Get-Content -LiteralPath $markerPath -Raw).Trim()
|
||||||
|
} else {
|
||||||
|
''
|
||||||
|
}
|
||||||
|
|
||||||
|
& $ollama show $modelName *> $null
|
||||||
|
$modelExists = ($LASTEXITCODE -eq 0)
|
||||||
|
|
||||||
|
if ($modelExists -and ($recordedHash -eq $desiredHash)) {
|
||||||
|
Write-Output "unchanged: $modelName already matches managed Modelfile"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
& $ollama pull $baseModel
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "ollama pull failed for $baseModel"
|
||||||
|
}
|
||||||
|
|
||||||
|
& $ollama create $modelName -f $modelfilePath
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "ollama create failed for $modelName"
|
||||||
|
}
|
||||||
|
|
||||||
|
Set-Content -LiteralPath $markerPath -Value $desiredHash -Encoding ascii -NoNewline
|
||||||
|
Write-Output "changed: created or updated $modelName from $baseModel"
|
||||||
|
args:
|
||||||
|
executable: powershell.exe
|
||||||
|
loop: "{{ prole_ollama_models }}"
|
||||||
|
register: ollama_model_results
|
||||||
|
changed_when: "'changed:' in ollama_model_results.stdout"
|
||||||
|
when: not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Verify managed Ollama model variants
|
||||||
|
ansible.windows.win_shell: |
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
& '{{ ollama_resolved_exe }}' list
|
||||||
|
args:
|
||||||
|
executable: powershell.exe
|
||||||
|
register: ollama_list_result
|
||||||
|
changed_when: false
|
||||||
|
when: not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Show Ollama model list
|
||||||
|
ansible.builtin.debug:
|
||||||
|
var: ollama_list_result.stdout_lines
|
||||||
|
when: not ansible_check_mode
|
||||||
@ -179,11 +179,10 @@ BEGIN
|
|||||||
|
|
||||||
v_uuid := knoe.ekosystem_id(0, 0); -- root authority mints the UUID
|
v_uuid := knoe.ekosystem_id(0, 0); -- root authority mints the UUID
|
||||||
|
|
||||||
INSERT INTO knoe.tenants (tenant_id, uuid, name)
|
RETURN QUERY
|
||||||
VALUES (v_tenant_id, v_uuid, p_name)
|
INSERT INTO knoe.tenants (tenant_id, uuid, name)
|
||||||
RETURNING tenants.tenant_id, tenants.uuid, tenants.name;
|
VALUES (v_tenant_id::smallint, v_uuid, p_name)
|
||||||
|
RETURNING tenants.tenant_id::integer, tenants.uuid, tenants.name;
|
||||||
RETURN NEXT;
|
|
||||||
END;
|
END;
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
|
|||||||
99
knoe-db/schema/migrations/001_prole_vector_schema.sql
Normal file
99
knoe-db/schema/migrations/001_prole_vector_schema.sql
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
-- Migration 001 — prole ekosystem vector schema
|
||||||
|
-- Tenant: prole (tenant_id=1, uuid=000necda5b3a6tc2)
|
||||||
|
-- Project embedding store UUID: 000nectf23m7865j (minted with tenant_id=1)
|
||||||
|
-- Embedding model: mxbai-embed-large-v1 (dim=1024)
|
||||||
|
--
|
||||||
|
-- Run from ~/dev/prole/knoe-db:
|
||||||
|
-- psql "$DSN" -v ON_ERROR_STOP=1 -f schema/migrations/001_prole_vector_schema.sql
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
-- Drop placeholder schema created before project UUID was minted
|
||||||
|
DROP SCHEMA IF EXISTS "p_000necda5b3a6tc2" CASCADE;
|
||||||
|
DROP ROLE IF EXISTS "u_000necda5b3a6tc2";
|
||||||
|
|
||||||
|
-- Service account
|
||||||
|
DO $$ BEGIN
|
||||||
|
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'u_000nectf23m7865j') THEN
|
||||||
|
CREATE ROLE "u_000nectf23m7865j"
|
||||||
|
LOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- Project namespace
|
||||||
|
CREATE SCHEMA IF NOT EXISTS "p_000nectf23m7865j" AUTHORIZATION knoe;
|
||||||
|
GRANT USAGE ON SCHEMA "p_000nectf23m7865j" TO "u_000nectf23m7865j";
|
||||||
|
|
||||||
|
-- Chunks: raw indexed text from repositories
|
||||||
|
CREATE TABLE IF NOT EXISTS "p_000nectf23m7865j".chunks (
|
||||||
|
ekosystem_uuid text NOT NULL DEFAULT '000nectf23m7865j',
|
||||||
|
id text NOT NULL,
|
||||||
|
repo text NOT NULL,
|
||||||
|
path text NOT NULL,
|
||||||
|
lang text,
|
||||||
|
line_start integer,
|
||||||
|
line_end integer,
|
||||||
|
content text NOT NULL,
|
||||||
|
header text NOT NULL DEFAULT '',
|
||||||
|
contributor_id text NOT NULL DEFAULT 'system',
|
||||||
|
indexed_at timestamptz NOT NULL DEFAULT now(),
|
||||||
|
PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_chunks_repo
|
||||||
|
ON "p_000nectf23m7865j".chunks (repo, path);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_chunks_contributor
|
||||||
|
ON "p_000nectf23m7865j".chunks (contributor_id);
|
||||||
|
|
||||||
|
-- Per-contributor embeddings
|
||||||
|
CREATE TABLE IF NOT EXISTS "p_000nectf23m7865j".embeddings (
|
||||||
|
ekosystem_uuid text NOT NULL DEFAULT '000nectf23m7865j',
|
||||||
|
id bigserial PRIMARY KEY,
|
||||||
|
chunk_id text NOT NULL
|
||||||
|
REFERENCES "p_000nectf23m7865j".chunks(id) ON DELETE CASCADE,
|
||||||
|
contributor_id text NOT NULL DEFAULT 'system',
|
||||||
|
embedding vector(1024) NOT NULL,
|
||||||
|
created_at timestamptz NOT NULL DEFAULT now()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Merged canonical embeddings — searched by MCP
|
||||||
|
CREATE TABLE IF NOT EXISTS "p_000nectf23m7865j".embeddings_merged (
|
||||||
|
ekosystem_uuid text NOT NULL DEFAULT '000nectf23m7865j',
|
||||||
|
id bigserial PRIMARY KEY,
|
||||||
|
chunk_id text NOT NULL
|
||||||
|
REFERENCES "p_000nectf23m7865j".chunks(id) ON DELETE CASCADE,
|
||||||
|
embedding vector(1024) NOT NULL,
|
||||||
|
source_contributor_id text NOT NULL DEFAULT 'system',
|
||||||
|
merged_at timestamptz NOT NULL DEFAULT now()
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_merged_hnsw
|
||||||
|
ON "p_000nectf23m7865j".embeddings_merged
|
||||||
|
USING hnsw (embedding vector_cosine_ops)
|
||||||
|
WITH (m = 16, ef_construction = 64);
|
||||||
|
|
||||||
|
-- Grants
|
||||||
|
GRANT SELECT, INSERT, UPDATE, DELETE
|
||||||
|
ON ALL TABLES IN SCHEMA "p_000nectf23m7865j" TO knoe;
|
||||||
|
GRANT USAGE, SELECT
|
||||||
|
ON ALL SEQUENCES IN SCHEMA "p_000nectf23m7865j" TO knoe;
|
||||||
|
|
||||||
|
-- Cross-ekosystem grant: tenant 0 (knoe.dev MCP) can search this store
|
||||||
|
SELECT knoe.grant_object(
|
||||||
|
'000nectf23m7865j',
|
||||||
|
'embedding',
|
||||||
|
0::smallint,
|
||||||
|
'{SELECT}'::text[],
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
-- Verify
|
||||||
|
SELECT (d).tenant_id AS owner_tenant, '000nectf23m7865j' AS project_uuid
|
||||||
|
FROM (SELECT knoe.ekosystem_decode('000nectf23m7865j') d) x;
|
||||||
|
|
||||||
|
SELECT direction, object_uuid, object_kind, privileges
|
||||||
|
FROM knoe.my_grants(1::smallint);
|
||||||
|
|
||||||
|
SELECT schemaname, tablename
|
||||||
|
FROM pg_tables WHERE schemaname = 'p_000nectf23m7865j'
|
||||||
|
ORDER BY tablename;
|
||||||
Loading…
Reference in New Issue
Block a user