Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fd6df322f6 | |||
| 20d279598a | |||
| 9dade105b6 | |||
| a77578e243 | |||
| ada8af1ccc | |||
| 99c5ce6e94 | |||
| 163ea8a48c | |||
| b210013891 | |||
| 635daaca9c | |||
| 9f9f271e78 | |||
| 343fcd20bc | |||
| 3ad9dc7df7 | |||
| cec2aa7168 | |||
| f47c92a71a | |||
| af594a9162 | |||
| 2589183775 | |||
| dcc0bf550a | |||
| d4609c7baa | |||
| 98603f5ae8 | |||
| bdc07be344 | |||
| 4a44c24bfb | |||
| ac1991fe8c | |||
| f2b63f0048 | |||
| 774688cfb9 | |||
| aa2404b3f7 | |||
| 94b55d667c | |||
| f73c913498 | |||
| 588ca11f57 | |||
| 9e178d8371 | |||
| aaada3770a | |||
| a0299c0a89 |
+235
-51
@@ -9,8 +9,10 @@ on:
|
|||||||
- main
|
- main
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
# Same group as deploy-gke so builds and deploys queue behind each other.
|
# Serialize all activity on the single GCE runner.
|
||||||
# Prevents concurrent Docker daemon exhaustion on the single GCE runner.
|
# With build+deploy in the same workflow, a new push queues a single
|
||||||
|
# workflow instance — not two competing ones — so the deploy job is
|
||||||
|
# never orphaned by a cancellation race.
|
||||||
concurrency:
|
concurrency:
|
||||||
group: neuron-runner
|
group: neuron-runner
|
||||||
cancel-in-progress: false
|
cancel-in-progress: false
|
||||||
@@ -29,12 +31,6 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Checkout foundation/el (ELP source for soul.el imports)
|
|
||||||
run: |
|
|
||||||
git clone https://git.neuralplatform.ai/neuron-technologies/el.git \
|
|
||||||
--depth=1 --branch=main \
|
|
||||||
../foundation/el
|
|
||||||
|
|
||||||
- name: Install build dependencies
|
- name: Install build dependencies
|
||||||
run: |
|
run: |
|
||||||
apt-get update -qq
|
apt-get update -qq
|
||||||
@@ -43,7 +39,7 @@ jobs:
|
|||||||
> /etc/apt/sources.list.d/google-cloud-sdk.list
|
> /etc/apt/sources.list.d/google-cloud-sdk.list
|
||||||
apt-get update -qq && apt-get install -y google-cloud-cli
|
apt-get update -qq && apt-get install -y google-cloud-cli
|
||||||
|
|
||||||
- name: Download El SDK from Artifact Registry
|
- name: Download El runtime from Artifact Registry
|
||||||
env:
|
env:
|
||||||
GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
|
GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
|
||||||
run: |
|
run: |
|
||||||
@@ -51,10 +47,12 @@ jobs:
|
|||||||
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
|
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
|
||||||
gcloud config set project neuron-785695
|
gcloud config set project neuron-785695
|
||||||
|
|
||||||
rm -rf /opt/el/dist /opt/el/runtime
|
rm -rf /opt/el/runtime
|
||||||
mkdir -p /opt/el/dist/platform /opt/el/dist/bin /opt/el/runtime
|
mkdir -p /opt/el/runtime
|
||||||
|
|
||||||
# Get latest version of each package
|
# Get latest version of each runtime package (elc/elb not needed — we compile
|
||||||
|
# dist/soul.c directly; running elb on Linux OOM-kills the runner, and we
|
||||||
|
# always use the repo's pre-built soul.c anyway).
|
||||||
get_latest() {
|
get_latest() {
|
||||||
gcloud artifacts versions list \
|
gcloud artifacts versions list \
|
||||||
--repository=foundation-prod \
|
--repository=foundation-prod \
|
||||||
@@ -66,22 +64,10 @@ jobs:
|
|||||||
--format="value(name)" 2>/dev/null | awk -F/ '{print $NF}'
|
--format="value(name)" 2>/dev/null | awk -F/ '{print $NF}'
|
||||||
}
|
}
|
||||||
|
|
||||||
ELC_VER=$(get_latest el-elc)
|
|
||||||
ELB_VER=$(get_latest el-elb)
|
|
||||||
RC_VER=$(get_latest el-runtime-c)
|
RC_VER=$(get_latest el-runtime-c)
|
||||||
RH_VER=$(get_latest el-runtime-h)
|
RH_VER=$(get_latest el-runtime-h)
|
||||||
|
|
||||||
echo "Downloading elc@${ELC_VER} elb@${ELB_VER} runtime@${RC_VER}"
|
echo "Downloading runtime@${RC_VER}"
|
||||||
|
|
||||||
gcloud artifacts generic download \
|
|
||||||
--repository=foundation-prod --location=us-central1 --project=neuron-785695 \
|
|
||||||
--package=el-elc --version="${ELC_VER}" \
|
|
||||||
--destination=/opt/el/dist/platform/
|
|
||||||
|
|
||||||
gcloud artifacts generic download \
|
|
||||||
--repository=foundation-prod --location=us-central1 --project=neuron-785695 \
|
|
||||||
--package=el-elb --version="${ELB_VER}" \
|
|
||||||
--destination=/opt/el/dist/bin/
|
|
||||||
|
|
||||||
gcloud artifacts generic download \
|
gcloud artifacts generic download \
|
||||||
--repository=foundation-prod --location=us-central1 --project=neuron-785695 \
|
--repository=foundation-prod --location=us-central1 --project=neuron-785695 \
|
||||||
@@ -93,39 +79,20 @@ jobs:
|
|||||||
--package=el-runtime-h --version="${RH_VER}" \
|
--package=el-runtime-h --version="${RH_VER}" \
|
||||||
--destination=/opt/el/runtime/
|
--destination=/opt/el/runtime/
|
||||||
|
|
||||||
# Downloaded files keep original names; rename to canonical paths
|
|
||||||
mv /opt/el/dist/platform/elc* /opt/el/dist/platform/elc 2>/dev/null || true
|
|
||||||
mv /opt/el/dist/bin/elb* /opt/el/dist/bin/elb 2>/dev/null || true
|
|
||||||
mv /opt/el/runtime/el_runtime.c* /opt/el/runtime/el_runtime.c 2>/dev/null || true
|
mv /opt/el/runtime/el_runtime.c* /opt/el/runtime/el_runtime.c 2>/dev/null || true
|
||||||
mv /opt/el/runtime/el_runtime.h* /opt/el/runtime/el_runtime.h 2>/dev/null || true
|
mv /opt/el/runtime/el_runtime.h* /opt/el/runtime/el_runtime.h 2>/dev/null || true
|
||||||
|
echo "El runtime ready: $(ls /opt/el/runtime/)"
|
||||||
chmod +x /opt/el/dist/platform/elc /opt/el/dist/bin/elb
|
|
||||||
echo "El SDK ready"
|
|
||||||
/opt/el/dist/platform/elc --version || true
|
|
||||||
|
|
||||||
- name: Build neuron soul binary
|
- name: Build neuron soul binary
|
||||||
run: |
|
run: |
|
||||||
ELB=/opt/el/dist/bin/elb
|
|
||||||
ELC=/opt/el/dist/platform/elc
|
|
||||||
RUNTIME=/opt/el/runtime
|
RUNTIME=/opt/el/runtime
|
||||||
|
|
||||||
# Preserve the pre-compiled dist/soul.c from the repo before running elb.
|
# Compile the self-contained translation unit directly from dist/soul.c.
|
||||||
# elb may overwrite it during compilation; we always want the repo version
|
# dist/soul.c is the authoritative combined unit maintained in the repo —
|
||||||
# since it contains the patched self-contained translation unit (all modules
|
# regenerated on macOS by running elb (which succeeds on arm64/macOS ld but
|
||||||
# inlined, workspace scope fix, agentic dedup fix, etc.).
|
# fails on Linux due to duplicate strong symbols). We skip the elb step here
|
||||||
cp dist/soul.c /tmp/soul.c.prebuilt
|
# entirely: elb on Linux would OOM the runner (elc uses 24GB+ virtual memory
|
||||||
|
# on a 16GB host) and we always restore from the repo's soul.c anyway.
|
||||||
# Compile all El modules to C via elb.
|
|
||||||
# elb fails at link on Linux (GNU ld rejects duplicate strong symbols that
|
|
||||||
# macOS ld accepts silently) — that's expected and captured with || true.
|
|
||||||
$ELB --elc=$ELC --runtime=$RUNTIME/el_runtime.c || true
|
|
||||||
|
|
||||||
# Restore the repo's self-contained soul.c — elb may have overwritten it
|
|
||||||
# with a partial (non-inlined) version that lacks module-level definitions.
|
|
||||||
cp /tmp/soul.c.prebuilt dist/soul.c
|
|
||||||
|
|
||||||
# Compile the self-contained translation unit. No --allow-multiple-definition
|
|
||||||
# needed since soul.c inlines all modules.
|
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
cc -O2 -DHAVE_CURL \
|
cc -O2 -DHAVE_CURL \
|
||||||
-I$RUNTIME \
|
-I$RUNTIME \
|
||||||
@@ -163,3 +130,220 @@ jobs:
|
|||||||
|
|
||||||
echo "Published neuron-soul@${VERSION}"
|
echo "Published neuron-soul@${VERSION}"
|
||||||
rm -f /tmp/gcp-key.json
|
rm -f /tmp/gcp-key.json
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
# Only deploy on push to main, not on PRs or manual workflow_dispatch without intent.
|
||||||
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||||
|
|
||||||
|
env:
|
||||||
|
USE_GKE_GCLOUD_AUTH_PLUGIN: "True"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Free disk space
|
||||||
|
run: |
|
||||||
|
df -h /
|
||||||
|
docker system prune -af --volumes 2>/dev/null || true
|
||||||
|
rm -rf /tmp/.act-* /tmp/act-* 2>/dev/null || true
|
||||||
|
df -h /
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
|
ca-certificates curl apt-transport-https kubectl
|
||||||
|
echo "deb [trusted=yes] https://packages.cloud.google.com/apt cloud-sdk main" \
|
||||||
|
> /etc/apt/sources.list.d/google-cloud-sdk.list
|
||||||
|
apt-get update -qq && apt-get install -y google-cloud-cli google-cloud-cli-gke-gcloud-auth-plugin
|
||||||
|
|
||||||
|
- name: Authenticate to GCP
|
||||||
|
env:
|
||||||
|
GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
|
||||||
|
run: |
|
||||||
|
echo "${GCP_SA_KEY}" > /tmp/gcp-key.json
|
||||||
|
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
|
||||||
|
gcloud config set project neuron-785695
|
||||||
|
gcloud auth configure-docker us-central1-docker.pkg.dev --quiet
|
||||||
|
|
||||||
|
- name: Get GKE credentials
|
||||||
|
run: |
|
||||||
|
gcloud container clusters get-credentials neuron-platform \
|
||||||
|
--region=us-central1 \
|
||||||
|
--project=neuron-785695
|
||||||
|
|
||||||
|
- name: Determine image tag and slot
|
||||||
|
id: vars
|
||||||
|
run: |
|
||||||
|
# GITEA_SHA is set by the Gitea runner; fall back to GITHUB_SHA for
|
||||||
|
# compatibility with older Forgejo/Gitea versions.
|
||||||
|
RAW_SHA="${GITEA_SHA:-${GITHUB_SHA:-}}"
|
||||||
|
SHA="${RAW_SHA:0:8}"
|
||||||
|
if [ -z "$SHA" ]; then
|
||||||
|
# Last resort: read from git directly
|
||||||
|
SHA=$(git rev-parse --short=8 HEAD 2>/dev/null || echo "unknown")
|
||||||
|
fi
|
||||||
|
IMAGE="us-central1-docker.pkg.dev/neuron-785695/neuron-api/neuron-soul:${SHA}"
|
||||||
|
echo "sha=${SHA}" >> "$GITEA_OUTPUT"
|
||||||
|
echo "image=${IMAGE}" >> "$GITEA_OUTPUT"
|
||||||
|
|
||||||
|
# Determine which slot is currently idle (0 replicas = idle slot)
|
||||||
|
# If both are at 0 (fresh deploy), default to blue
|
||||||
|
BLUE_REPLICAS=$(kubectl get deployment/neuron-mcp-blue \
|
||||||
|
-n neuron-prod \
|
||||||
|
-o jsonpath='{.spec.replicas}' 2>/dev/null || echo "0")
|
||||||
|
GREEN_REPLICAS=$(kubectl get deployment/neuron-mcp-green \
|
||||||
|
-n neuron-prod \
|
||||||
|
-o jsonpath='{.spec.replicas}' 2>/dev/null || echo "0")
|
||||||
|
|
||||||
|
echo " Blue replicas: ${BLUE_REPLICAS}"
|
||||||
|
echo " Green replicas: ${GREEN_REPLICAS}"
|
||||||
|
|
||||||
|
if [ "${GREEN_REPLICAS}" -eq 0 ] && [ "${BLUE_REPLICAS}" -gt 0 ]; then
|
||||||
|
SLOT="green"
|
||||||
|
elif [ "${BLUE_REPLICAS}" -eq 0 ] && [ "${GREEN_REPLICAS}" -gt 0 ]; then
|
||||||
|
SLOT="blue"
|
||||||
|
else
|
||||||
|
# Fresh cluster or both idle — deploy to blue first
|
||||||
|
SLOT="blue"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "slot=${SLOT}" >> "$GITEA_OUTPUT"
|
||||||
|
echo " Deploying to slot: ${SLOT}"
|
||||||
|
|
||||||
|
- name: Prepare build artifacts
|
||||||
|
run: |
|
||||||
|
# Pre-download soul binary and El SDK so the Dockerfile can COPY them
|
||||||
|
# from the build context instead of authenticating inside the build.
|
||||||
|
mkdir -p build-artifacts
|
||||||
|
|
||||||
|
# ── soul binary ────────────────────────────────────────────────────────
|
||||||
|
# The build job (same workflow run) just published this version.
|
||||||
|
SOUL_VER=$(gcloud artifacts versions list \
|
||||||
|
--repository=foundation-prod \
|
||||||
|
--location=us-central1 \
|
||||||
|
--project=neuron-785695 \
|
||||||
|
--package=neuron-soul \
|
||||||
|
--sort-by="~createTime" \
|
||||||
|
--limit=1 \
|
||||||
|
--format="value(name)" 2>/dev/null | awk -F/ '{print $NF}')
|
||||||
|
echo "Downloading neuron-soul@${SOUL_VER}"
|
||||||
|
gcloud artifacts generic download \
|
||||||
|
--repository=foundation-prod \
|
||||||
|
--location=us-central1 \
|
||||||
|
--project=neuron-785695 \
|
||||||
|
--package=neuron-soul \
|
||||||
|
--version="${SOUL_VER}" \
|
||||||
|
--destination=build-artifacts/
|
||||||
|
mv build-artifacts/neuron* build-artifacts/neuron 2>/dev/null || true
|
||||||
|
chmod +x build-artifacts/neuron
|
||||||
|
|
||||||
|
# ── El SDK (for engram source compilation inside the Docker build) ────
|
||||||
|
ELC_VER=$(gcloud artifacts versions list \
|
||||||
|
--repository=foundation-prod --location=us-central1 --project=neuron-785695 \
|
||||||
|
--package=el-elc --sort-by="~createTime" --limit=1 \
|
||||||
|
--format="value(name)" 2>/dev/null | awk -F/ '{print $NF}')
|
||||||
|
gcloud artifacts generic download \
|
||||||
|
--repository=foundation-prod --location=us-central1 --project=neuron-785695 \
|
||||||
|
--package=el-elc --version="${ELC_VER}" --destination=build-artifacts/
|
||||||
|
mv build-artifacts/elc* build-artifacts/elc 2>/dev/null || true
|
||||||
|
chmod +x build-artifacts/elc
|
||||||
|
|
||||||
|
RC_VER=$(gcloud artifacts versions list \
|
||||||
|
--repository=foundation-prod --location=us-central1 --project=neuron-785695 \
|
||||||
|
--package=el-runtime-c --sort-by="~createTime" --limit=1 \
|
||||||
|
--format="value(name)" 2>/dev/null | awk -F/ '{print $NF}')
|
||||||
|
gcloud artifacts generic download \
|
||||||
|
--repository=foundation-prod --location=us-central1 --project=neuron-785695 \
|
||||||
|
--package=el-runtime-c --version="${RC_VER}" --destination=build-artifacts/
|
||||||
|
mv build-artifacts/el_runtime.c* build-artifacts/el_runtime.c 2>/dev/null || true
|
||||||
|
|
||||||
|
RH_VER=$(gcloud artifacts versions list \
|
||||||
|
--repository=foundation-prod --location=us-central1 --project=neuron-785695 \
|
||||||
|
--package=el-runtime-h --sort-by="~createTime" --limit=1 \
|
||||||
|
--format="value(name)" 2>/dev/null | awk -F/ '{print $NF}')
|
||||||
|
gcloud artifacts generic download \
|
||||||
|
--repository=foundation-prod --location=us-central1 --project=neuron-785695 \
|
||||||
|
--package=el-runtime-h --version="${RH_VER}" --destination=build-artifacts/
|
||||||
|
mv build-artifacts/el_runtime.h* build-artifacts/el_runtime.h 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "Build artifacts ready:"
|
||||||
|
ls -lh build-artifacts/
|
||||||
|
|
||||||
|
- name: Clone engram source for Docker build context
|
||||||
|
run: |
|
||||||
|
# The Dockerfile builds engram from source (no published AR package).
|
||||||
|
# Clone the engram repo into ./engram/ so it's available in the build context.
|
||||||
|
git clone http://34.31.145.131/neuron-technologies/engram.git \
|
||||||
|
--depth=1 --branch=main \
|
||||||
|
engram
|
||||||
|
echo "Engram source ready at ./engram/src/server.el"
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
run: |
|
||||||
|
IMAGE="${{ steps.vars.outputs.image }}"
|
||||||
|
|
||||||
|
echo "Building ${IMAGE}..."
|
||||||
|
docker build \
|
||||||
|
--tag "${IMAGE}" \
|
||||||
|
--tag "us-central1-docker.pkg.dev/neuron-785695/neuron-api/neuron-soul:latest" \
|
||||||
|
.
|
||||||
|
|
||||||
|
echo "Pushing ${IMAGE}..."
|
||||||
|
docker push "${IMAGE}"
|
||||||
|
docker push "us-central1-docker.pkg.dev/neuron-785695/neuron-api/neuron-soul:latest"
|
||||||
|
|
||||||
|
- name: Blue-green deploy to GKE
|
||||||
|
run: |
|
||||||
|
chmod +x scripts/blue-green-deploy.sh
|
||||||
|
scripts/blue-green-deploy.sh \
|
||||||
|
--image "${{ steps.vars.outputs.image }}" \
|
||||||
|
--slot "${{ steps.vars.outputs.slot }}"
|
||||||
|
|
||||||
|
- name: Update infrastructure manifests
|
||||||
|
if: success()
|
||||||
|
env:
|
||||||
|
INFRA_GIT_TOKEN: ${{ secrets.INFRA_GIT_TOKEN }}
|
||||||
|
run: |
|
||||||
|
SLOT="${{ steps.vars.outputs.slot }}"
|
||||||
|
if [ "$SLOT" = "blue" ]; then IDLE="green"; else IDLE="blue"; fi
|
||||||
|
|
||||||
|
git clone "http://${INFRA_GIT_TOKEN}@34.31.145.131/neuron-technologies/infrastructure.git" \
|
||||||
|
--depth=1 --branch=main /tmp/infra-update
|
||||||
|
|
||||||
|
cd /tmp/infra-update
|
||||||
|
|
||||||
|
DEPLOY_DIR="platform/k8s/neuron-mcp"
|
||||||
|
sed -i "s/^ replicas: .*/ replicas: 1/" "${DEPLOY_DIR}/deployment-${SLOT}.yaml"
|
||||||
|
sed -i "s/^ replicas: .*/ replicas: 0/" "${DEPLOY_DIR}/deployment-${IDLE}.yaml"
|
||||||
|
echo " deployment-${SLOT}.yaml: replicas set to 1"
|
||||||
|
echo " deployment-${IDLE}.yaml: replicas set to 0"
|
||||||
|
|
||||||
|
git config user.email "ci@neurontechnologies.ai"
|
||||||
|
git config user.name "Neuron CI"
|
||||||
|
git add "${DEPLOY_DIR}/deployment-blue.yaml" "${DEPLOY_DIR}/deployment-green.yaml"
|
||||||
|
git diff --staged --quiet && { echo "No manifest changes needed"; exit 0; }
|
||||||
|
git commit -m "ci: neuron-mcp replica sync after blue-green swap to ${SLOT}"
|
||||||
|
git push origin main
|
||||||
|
echo "Infrastructure manifests updated: ${SLOT}=1, ${IDLE}=0"
|
||||||
|
|
||||||
|
- name: Verify deployment
|
||||||
|
run: |
|
||||||
|
SLOT="${{ steps.vars.outputs.slot }}"
|
||||||
|
echo "Verifying neuron-mcp-${SLOT} is healthy..."
|
||||||
|
kubectl rollout status deployment/"neuron-mcp-${SLOT}" \
|
||||||
|
--namespace=neuron-prod \
|
||||||
|
--timeout=8m
|
||||||
|
|
||||||
|
echo "Active service endpoints:"
|
||||||
|
kubectl get endpoints neuron-mcp -n neuron-prod
|
||||||
|
|
||||||
|
echo "Pod status:"
|
||||||
|
kubectl get pods -n neuron-prod -l app=neuron-mcp
|
||||||
|
|
||||||
|
- name: Cleanup
|
||||||
|
if: always()
|
||||||
|
run: rm -f /tmp/gcp-key.json
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
name: Deploy Soul to GKE
|
name: Deploy Soul to GKE (manual)
|
||||||
|
|
||||||
# Triggers on push to main — after the soul binary is built and published
|
# MANUAL OVERRIDE ONLY — push-triggered deploys now run as the 'deploy' job
|
||||||
# by ci.yaml, this workflow builds the Docker image and blue-green deploys
|
# in ci.yaml (needs: build), which eliminates the two-workflow concurrency
|
||||||
# to the neuron-prod namespace on GKE.
|
# race that was cancelling queued deploy runs.
|
||||||
#
|
#
|
||||||
# This workflow runs AFTER ci.yaml has published the neuron-soul generic
|
# Use this workflow only when you need to deploy a specific slot manually
|
||||||
# artifact to Artifact Registry. The Docker build downloads that binary.
|
# (e.g. rollback, force a slot override) without triggering a full CI build.
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
slot:
|
slot:
|
||||||
@@ -18,8 +15,7 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
default: "green"
|
default: "green"
|
||||||
|
|
||||||
# Serialize all builds on this runner — concurrent jobs exhaust the Docker daemon.
|
# Manual deploys still share the runner serialization group.
|
||||||
# A queued deploy runs after the in-progress build finishes.
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: neuron-runner
|
group: neuron-runner
|
||||||
cancel-in-progress: false
|
cancel-in-progress: false
|
||||||
|
|||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
# Compiled binaries
|
||||||
|
dist/neuron
|
||||||
|
dist/neuron.backup-*
|
||||||
|
dist/*.backup-*
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
+58
-34
@@ -152,6 +152,27 @@ fn emit_heartbeat() -> Void {
|
|||||||
// a reserved/conflicting name in EL that compiles to EL_NULL at call sites.
|
// a reserved/conflicting name in EL that compiles to EL_NULL at call sites.
|
||||||
//
|
//
|
||||||
// Returns true if any nodes were activated.
|
// Returns true if any nodes were activated.
|
||||||
|
// auto_term_try_slot — attempt to set cseed_auto from one WM slot.
|
||||||
|
// Only writes to cseed_auto if node_type is Memory, BacklogItem, or Entity
|
||||||
|
// AND the first word of the label is > 3 chars (guards bracket-prefixed labels).
|
||||||
|
// Designed to be called in reverse slot order (highest index first) so that
|
||||||
|
// the lowest-indexed slot (highest WM weight) wins by last-write semantics.
|
||||||
|
fn auto_term_try_slot(slot_type: String, slot_lbl: String) -> Void {
|
||||||
|
state_set("_ats_ok", "0")
|
||||||
|
if str_eq(slot_type, "Memory") { state_set("_ats_ok", "1") }
|
||||||
|
if str_eq(slot_type, "BacklogItem") { state_set("_ats_ok", "1") }
|
||||||
|
if str_eq(slot_type, "Entity") { state_set("_ats_ok", "1") }
|
||||||
|
if str_eq(state_get("_ats_ok"), "1") {
|
||||||
|
if !str_eq(slot_lbl, "") {
|
||||||
|
let sp: Int = str_find_chars(slot_lbl, " :([")
|
||||||
|
if sp > 3 {
|
||||||
|
state_set("cseed_auto", str_slice(slot_lbl, 0, sp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
fn proactive_curiosity() -> Bool {
|
fn proactive_curiosity() -> Bool {
|
||||||
let ts: Int = time_now()
|
let ts: Int = time_now()
|
||||||
// Rotate seed set every minute using wall clock: (minutes_since_epoch) % 4.
|
// Rotate seed set every minute using wall clock: (minutes_since_epoch) % 4.
|
||||||
@@ -210,43 +231,46 @@ fn proactive_curiosity() -> Bool {
|
|||||||
let found_c: Int = json_array_len(results_c)
|
let found_c: Int = json_array_len(results_c)
|
||||||
let found: Int = found_a + found_b + found_c
|
let found: Int = found_a + found_b + found_c
|
||||||
|
|
||||||
// WM-autobiographical 4th seed: extract the first word from the top working-memory
|
// WM-autobiographical 4th seed: scan top-10 WM nodes for the highest-ranked
|
||||||
// node's label and activate it as an additional term. This creates a self-referencing
|
// non-Knowledge node. Extract its first word as an additional curiosity term.
|
||||||
// curiosity loop — exploration radiates outward from whatever is most salient right now,
|
// This creates a self-referencing curiosity loop — exploration radiates outward
|
||||||
// mirroring the brain's default-mode-network resting-state dynamics. Breaks the fixed
|
// from whatever is most personally salient right now (Memory, BacklogItem, Entity),
|
||||||
// 4-set determinism that otherwise reinforces the same subgraph every rotation cycle.
|
// mirroring default-mode-network resting-state dynamics.
|
||||||
//
|
//
|
||||||
// str_find_chars finds the first space/colon/bracket delimiter. sp > 3 guards against
|
// WHY TOP-10 (2026-06-23 self-review): the old top-1 scan always returned a
|
||||||
// very short or bracket-prefixed labels like "[BacklogItem]" (sp=0, not > 3 → skipped).
|
// Knowledge node (WM is dominated by stable engram-metadata Knowledge nodes at
|
||||||
// EL scoping: state_set/state_get pattern used because let inside if creates inner scope.
|
// position [0]). Verified: Memory nodes consistently appear at WM positions [1],[2]
|
||||||
|
// with wm ~0.59. Scanning top-10 reliably finds at least one Memory/BacklogItem/Entity.
|
||||||
|
// Out-of-bounds json_array_get returns "" → json_get("","...") returns "" →
|
||||||
|
// auto_term_try_slot is a no-op → safe for WM sets smaller than 10.
|
||||||
//
|
//
|
||||||
// NODE TYPE FILTER (2026-06-19 self-review): only derive auto_term from Memory,
|
// NODE TYPE FILTER (2026-06-19): Knowledge nodes excluded as seeds — they create
|
||||||
// BacklogItem, or Entity nodes. Knowledge nodes are stable reference material —
|
// self-reinforcing loops (Knowledge node activates its own first word, stays dominant).
|
||||||
// using their first word as a curiosity seed creates a self-reinforcing loop: e.g.
|
// Only Memory/BacklogItem/Entity carry live contextual salience worth radiating from.
|
||||||
// "Numeric tier strings in Engram..." (a Knowledge node) -> auto_term="Numeric" ->
|
//
|
||||||
// activates all "Numeric" nodes -> keeps that Knowledge node dominant in WM forever.
|
// SLOT ORDER: call 9→0 so slot 0 (highest WM weight) wins by last-write semantics.
|
||||||
// Knowledge nodes should be REACHED by curiosity seeds, not drive them. Only dynamic
|
|
||||||
// personal/work nodes (Memory, BacklogItem, Entity) carry live contextual salience
|
|
||||||
// worth radiating from. (2026-06-11 origin; filter added 2026-06-19 self-review)
|
|
||||||
state_set("cseed_auto", "")
|
state_set("cseed_auto", "")
|
||||||
let wm_top_j: String = engram_wm_top_json(1)
|
let wm10: String = engram_wm_top_json(10)
|
||||||
let wm_top_n: String = json_array_get(wm_top_j, 0)
|
let wm10_n9: String = json_array_get(wm10, 9)
|
||||||
let wm_top_lbl: String = json_get(wm_top_n, "label")
|
let wm10_n8: String = json_array_get(wm10, 8)
|
||||||
let wm_top_type: String = json_get(wm_top_n, "node_type")
|
let wm10_n7: String = json_array_get(wm10, 7)
|
||||||
// state_set/state_get pattern: EL let-inside-if creates inner scope only.
|
let wm10_n6: String = json_array_get(wm10, 6)
|
||||||
state_set("allow_auto", "0")
|
let wm10_n5: String = json_array_get(wm10, 5)
|
||||||
if str_eq(wm_top_type, "Memory") { state_set("allow_auto", "1") }
|
let wm10_n4: String = json_array_get(wm10, 4)
|
||||||
if str_eq(wm_top_type, "BacklogItem") { state_set("allow_auto", "1") }
|
let wm10_n3: String = json_array_get(wm10, 3)
|
||||||
if str_eq(wm_top_type, "Entity") { state_set("allow_auto", "1") }
|
let wm10_n2: String = json_array_get(wm10, 2)
|
||||||
let allow_auto: String = state_get("allow_auto")
|
let wm10_n1: String = json_array_get(wm10, 1)
|
||||||
if str_eq(allow_auto, "1") {
|
let wm10_n0: String = json_array_get(wm10, 0)
|
||||||
if !str_eq(wm_top_lbl, "") {
|
auto_term_try_slot(json_get(wm10_n9, "node_type"), json_get(wm10_n9, "label"))
|
||||||
let sp: Int = str_find_chars(wm_top_lbl, " :([")
|
auto_term_try_slot(json_get(wm10_n8, "node_type"), json_get(wm10_n8, "label"))
|
||||||
if sp > 3 {
|
auto_term_try_slot(json_get(wm10_n7, "node_type"), json_get(wm10_n7, "label"))
|
||||||
state_set("cseed_auto", str_slice(wm_top_lbl, 0, sp))
|
auto_term_try_slot(json_get(wm10_n6, "node_type"), json_get(wm10_n6, "label"))
|
||||||
}
|
auto_term_try_slot(json_get(wm10_n5, "node_type"), json_get(wm10_n5, "label"))
|
||||||
}
|
auto_term_try_slot(json_get(wm10_n4, "node_type"), json_get(wm10_n4, "label"))
|
||||||
}
|
auto_term_try_slot(json_get(wm10_n3, "node_type"), json_get(wm10_n3, "label"))
|
||||||
|
auto_term_try_slot(json_get(wm10_n2, "node_type"), json_get(wm10_n2, "label"))
|
||||||
|
auto_term_try_slot(json_get(wm10_n1, "node_type"), json_get(wm10_n1, "label"))
|
||||||
|
auto_term_try_slot(json_get(wm10_n0, "node_type"), json_get(wm10_n0, "label"))
|
||||||
let auto_term: String = state_get("cseed_auto")
|
let auto_term: String = state_get("cseed_auto")
|
||||||
let results_auto: String = if str_eq(auto_term, "") { "[]" } else { engram_activate_json(auto_term, 1) }
|
let results_auto: String = if str_eq(auto_term, "") { "[]" } else { engram_activate_json(auto_term, 1) }
|
||||||
let found_auto: Int = json_array_len(results_auto)
|
let found_auto: Int = json_array_len(results_auto)
|
||||||
|
|||||||
@@ -233,125 +233,7 @@ fn engram_compile_ranked(nodes_json: String, max_nodes: Int) -> String {
|
|||||||
}
|
}
|
||||||
if str_eq(selected_nodes, "") { return "" }
|
if str_eq(selected_nodes, "") { return "" }
|
||||||
return "[" + selected_nodes + "]"
|
return "[" + selected_nodes + "]"
|
||||||
}ory.el"
|
|
||||||
|
|
||||||
fn chat_default_model() -> String {
|
|
||||||
let m: String = state_get("soul_model")
|
|
||||||
if !str_eq(m, "") {
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
let e: String = env("SOUL_LLM_MODEL")
|
|
||||||
if !str_eq(e, "") {
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
return "claude-sonnet-4-5"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// engram_score_node — compute a recency x relevance score for a single engram
|
|
||||||
// node JSON object. Higher is better. Score = salience * importance * recency_factor.
|
|
||||||
// recency_factor decays linearly over 30 days: nodes updated today score 1.0,
|
|
||||||
// nodes 30+ days old score 0.1 (floor). Nodes with no created_at score 0.5.
|
|
||||||
// This keeps fresh, high-salience nodes at the top and pushes stale low-signal
|
|
||||||
// nodes to the bottom so they get trimmed when we cap context size.
|
|
||||||
fn engram_score_node(node_json: String) -> Int {
|
|
||||||
let salience_str: String = json_get(node_json, "salience")
|
|
||||||
let importance_str: String = json_get(node_json, "importance")
|
|
||||||
let created_str: String = json_get(node_json, "created_at")
|
|
||||||
|
|
||||||
// parse_float_x100 handles 1- and 2-decimal floats correctly ("0.9" -> 90, "0.85" -> 85).
|
|
||||||
// Default 70 when field is absent; clamp to 0-100 range.
|
|
||||||
let salience_100: Int = if str_eq(salience_str, "") { 70 } else {
|
|
||||||
let s: Int = parse_float_x100(salience_str)
|
|
||||||
if s > 100 { 100 } else { if s < 0 { 0 } else { s } }
|
|
||||||
}
|
|
||||||
let importance_100: Int = if str_eq(importance_str, "") { 70 } else {
|
|
||||||
let v: Int = parse_float_x100(importance_str)
|
|
||||||
if v > 100 { 100 } else { if v < 0 { 0 } else { v } }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recency: decay from 100 (today) to 10 (30+ days). created_at is Unix seconds.
|
|
||||||
let now_ts: Int = time_now()
|
|
||||||
let recency_100: Int = if str_eq(created_str, "") { 50 } else {
|
|
||||||
let created_ts: Int = str_to_int(created_str)
|
|
||||||
let age_secs: Int = now_ts - created_ts
|
|
||||||
let age_days: Int = age_secs / 86400
|
|
||||||
let decay: Int = if age_days >= 30 { 10 } else { 100 - (age_days * 3) }
|
|
||||||
if decay < 10 { 10 } else { decay }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Combined score 0-1000000 (no floats): salience * importance * recency / 10000
|
|
||||||
return salience_100 * importance_100 * recency_100 / 10000
|
|
||||||
}
|
|
||||||
|
|
||||||
// engram_compile_ranked — build a context string from a JSON array of node objects,
|
|
||||||
// ordered best-first by score. Only nodes above threshold=25 are included.
|
|
||||||
// With corrected float parsing: sal=0.5 * imp=0.5 at max recency (100) scores exactly 25,
|
|
||||||
// so threshold=25 admits all nodes with at least moderate salience and importance while
|
|
||||||
// cutting near-zero noise. Lower values were masking the bug; 25 is correct post-fix.
|
|
||||||
// Returns at most max_nodes entries. max_nodes must not exceed 20 (sentinel limit).
|
|
||||||
fn engram_compile_ranked(nodes_json: String, max_nodes: Int) -> String {
|
|
||||||
if str_eq(nodes_json, "") { return "" }
|
|
||||||
if str_eq(nodes_json, "[]") { return "" }
|
|
||||||
let total: Int = json_array_len(nodes_json)
|
|
||||||
if total == 0 { return "" }
|
|
||||||
let selected_indices: String = ""
|
|
||||||
let selected_nodes: String = ""
|
|
||||||
let pass: Int = 0
|
|
||||||
while pass < max_nodes && pass < total {
|
|
||||||
let best_idx: Int = -1
|
|
||||||
let best_score: Int = -1
|
|
||||||
let ci: Int = 0
|
|
||||||
while ci < total {
|
|
||||||
let node: String = json_array_get(nodes_json, ci)
|
|
||||||
let score: Int = engram_score_node(node)
|
|
||||||
// Threshold 25: sal=0.5 * imp=0.5 * recency=1.0 -> 50*50*100/10000 = 25.
|
|
||||||
let above_thresh: Bool = score >= 25
|
|
||||||
// Check this index wasn't already selected (sentinel: look for idx marker)
|
|
||||||
let idx_marker: String = "\"_sel_" + int_to_str(ci) + "\""
|
|
||||||
let already_picked: Bool = str_contains(selected, idx_marker)
|
|
||||||
let is_better: Bool = score > best_score && above_thresh && !already_picked
|
|
||||||
let best_score = if is_better { score } else { best_score }
|
|
||||||
let best_idx = if is_better { ci } else { best_idx }
|
|
||||||
let ci = ci + 1
|
|
||||||
}
|
|
||||||
if best_idx < 0 {
|
|
||||||
let pass = total // break
|
|
||||||
} else {
|
|
||||||
let chosen: String = json_array_get(nodes_json, best_idx)
|
|
||||||
let sep: String = if str_eq(selected_nodes, "") { "" } else { "," }
|
|
||||||
let selected_nodes = selected_nodes + sep + chosen
|
|
||||||
let selected_indices = selected_indices + "|" + int_to_str(best_idx) + "|"
|
|
||||||
}
|
|
||||||
let pass = pass + 1
|
|
||||||
}
|
|
||||||
if str_eq(selected_nodes, "") { return "" }
|
|
||||||
return "[" + selected_nodes + "]"
|
|
||||||
}
|
|
||||||
|
|
||||||
if str_eq(selected, "") { return "" }
|
|
||||||
// Strip the _sel_N sentinel fields that were used for duplicate-detection bookkeeping.
|
|
||||||
// The sentinels have the form "\"_sel_N\":1," (trailing comma, space before next key).
|
|
||||||
// We injected them as the first field in each object, so the pattern is predictable.
|
|
||||||
// Because el has no regex, remove up to 20 possible sentinel variants by literal replace.
|
|
||||||
let clean: String = "[" + selected + "]"
|
|
||||||
let c0: String = str_replace(clean, "\"_sel_0\":1,", "")
|
|
||||||
let c1: String = str_replace(c0, "\"_sel_1\":1,", "")
|
|
||||||
let c2: String = str_replace(c1, "\"_sel_2\":1,", "")
|
|
||||||
let c3: String = str_replace(c2, "\"_sel_3\":1,", "")
|
|
||||||
let c4: String = str_replace(c3, "\"_sel_4\":1,", "")
|
|
||||||
let c5: String = str_replace(c4, "\"_sel_5\":1,", "")
|
|
||||||
let c6: String = str_replace(c5, "\"_sel_6\":1,", "")
|
|
||||||
let c7: String = str_replace(c6, "\"_sel_7\":1,", "")
|
|
||||||
let c8: String = str_replace(c7, "\"_sel_8\":1,", "")
|
|
||||||
let c9: String = str_replace(c8, "\"_sel_9\":1,", "")
|
|
||||||
let c10: String = str_replace(c9, "\"_sel_10\":1,", "")
|
|
||||||
let c11: String = str_replace(c10, "\"_sel_11\":1,", "")
|
|
||||||
let c12: String = str_replace(c11, "\"_sel_12\":1,", "")
|
|
||||||
let c13: String = str_replace(c12, "\"_sel_13\":1,", "")
|
|
||||||
let c14: String = str_replace(c13, "\"_sel_14\":1,", "")
|
|
||||||
return c14
|
|
||||||
}
|
|
||||||
|
|
||||||
// engram_split_topics — split message into sub-queries on explicit conjunctions.
|
// engram_split_topics — split message into sub-queries on explicit conjunctions.
|
||||||
// "health goals AND startup progress" becomes two independent searches.
|
// "health goals AND startup progress" becomes two independent searches.
|
||||||
fn engram_split_topics(message: String) -> String {
|
fn engram_split_topics(message: String) -> String {
|
||||||
@@ -495,6 +377,38 @@ fn engram_nodes_merge(a: String, b: String) -> String {
|
|||||||
return engram_dedup_nodes("[" + ai + "," + bi + "]")
|
return engram_dedup_nodes("[" + ai + "," + bi + "]")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// id_in_seen — true when node_id appears in the pipe-delimited seen set.
|
||||||
|
fn id_in_seen(node_id: String, seen: String) -> Bool {
|
||||||
|
if str_eq(node_id, "") { return false }
|
||||||
|
if str_eq(seen, "") { return false }
|
||||||
|
return str_contains(seen, "|" + node_id + "|")
|
||||||
|
}
|
||||||
|
|
||||||
|
// add_to_seen — append node_id to the pipe-delimited seen set.
|
||||||
|
fn add_to_seen(seen: String, node_id: String) -> String {
|
||||||
|
if str_eq(node_id, "") { return seen }
|
||||||
|
if id_in_seen(node_id, seen) { return seen }
|
||||||
|
return seen + "|" + node_id + "|"
|
||||||
|
}
|
||||||
|
|
||||||
|
// engram_extract_ids — extract the "id" field from each node in a JSON array,
|
||||||
|
// returning a pipe-delimited string suitable for id_in_seen / add_to_seen.
|
||||||
|
fn engram_extract_ids(nodes_json: String) -> String {
|
||||||
|
if str_eq(nodes_json, "") { return "" }
|
||||||
|
if str_eq(nodes_json, "[]") { return "" }
|
||||||
|
let total: Int = json_array_len(nodes_json)
|
||||||
|
if total == 0 { return "" }
|
||||||
|
let seen: String = ""
|
||||||
|
let i: Int = 0
|
||||||
|
while i < total {
|
||||||
|
let node: String = json_array_get(nodes_json, i)
|
||||||
|
let node_id: String = json_get(node, "id")
|
||||||
|
let seen = add_to_seen(seen, node_id)
|
||||||
|
let i = i + 1
|
||||||
|
}
|
||||||
|
return seen
|
||||||
|
}
|
||||||
|
|
||||||
// Q4 note: engram_compile has no cache or circuit-breaker at the EL layer.
|
// Q4 note: engram_compile has no cache or circuit-breaker at the EL layer.
|
||||||
// Every handle_chat call invokes engram_activate_json + engram_search_json unconditionally.
|
// Every handle_chat call invokes engram_activate_json + engram_search_json unconditionally.
|
||||||
// If the engram backend is repeatedly unreachable (e.g., during startup or after a crash),
|
// If the engram backend is repeatedly unreachable (e.g., during startup or after a crash),
|
||||||
@@ -584,6 +498,10 @@ fn engram_compile(intent: String) -> String {
|
|||||||
let merged: String = engram_nodes_merge(merged, recall_boost)
|
let merged: String = engram_nodes_merge(merged, recall_boost)
|
||||||
let merged_nodes: String = merged
|
let merged_nodes: String = merged
|
||||||
|
|
||||||
|
// Publish compiled IDs to state so session_preload can skip duplicate nodes.
|
||||||
|
let ids_from_merged: String = engram_extract_ids(merged_nodes)
|
||||||
|
state_set("engram_compile_seen_ids", ids_from_merged)
|
||||||
|
|
||||||
// Fallback: when all searches return nothing, fetch persona nodes.
|
// Fallback: when all searches return nothing, fetch persona nodes.
|
||||||
let scan_part: String = if str_eq(merged_nodes, "") || str_eq(merged_nodes, "[]") {
|
let scan_part: String = if str_eq(merged_nodes, "") || str_eq(merged_nodes, "[]") {
|
||||||
let persona_fallback: String = engram_search_json("soul:persona Persona identity", 5)
|
let persona_fallback: String = engram_search_json("soul:persona Persona identity", 5)
|
||||||
@@ -648,12 +566,8 @@ fn engram_compile(intent: String) -> String {
|
|||||||
let sep_ma: String = if !str_eq(main_part, "") && !str_eq(affective_part, "") { "\n" } else { "" }
|
let sep_ma: String = if !str_eq(main_part, "") && !str_eq(affective_part, "") { "\n" } else { "" }
|
||||||
let ctx: String = main_part + sep_ma + affective_part
|
let ctx: String = main_part + sep_ma + affective_part
|
||||||
|
|
||||||
// Q7 fix: store recall status so build_system_prompt can include a hint to the LLM
|
// Publish recall_status for build_system_prompt: "ok" when ctx has content, "empty" otherwise.
|
||||||
// distinguishing "no memories yet" (cold start) from "memory system unreachable".
|
let recall_status: String = if str_eq(ctx, "") { "empty" } else { "ok" }
|
||||||
// Values: "ok" | "empty" | "unavailable"
|
|
||||||
let any_ok: Bool = act_ok || srch_ok || scan_ok || affective_ok
|
|
||||||
let all_failed: Bool = act_failed && srch_failed
|
|
||||||
let recall_status: String = if any_ok { "ok" } else { if all_failed { "unavailable" } else { "empty" } }
|
|
||||||
state_set("engram_recall_status", recall_status)
|
state_set("engram_recall_status", recall_status)
|
||||||
|
|
||||||
if str_eq(ctx, "") {
|
if str_eq(ctx, "") {
|
||||||
@@ -715,6 +629,17 @@ fn build_system_prompt(ctx: String, chat_mode: Bool) -> String {
|
|||||||
"\n\n[IDENTITY GRAPH — who you are, loaded from your engram]\n" + id_ctx
|
"\n\n[IDENTITY GRAPH — who you are, loaded from your engram]\n" + id_ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// soul_affective_context is loaded at boot by load_identity_context() with BellEvent/
|
||||||
|
// PositiveEvent nodes from the last 7 days. Surfaced here so the LLM sees historical
|
||||||
|
// emotional patterns from prior sessions at every turn.
|
||||||
|
// Issue 1 fix: declare affective_boot_block before it is referenced in the return.
|
||||||
|
let boot_aff_ctx: String = state_get("soul_affective_context")
|
||||||
|
let affective_boot_block: String = if str_eq(boot_aff_ctx, "") {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
"\n\n[CROSS-SESSION EMOTIONAL CONTEXT — from prior sessions]\n" + boot_aff_ctx
|
||||||
|
}
|
||||||
|
|
||||||
// Q7 fix: if recall produced no results, include a hint so the LLM can respond
|
// Q7 fix: if recall produced no results, include a hint so the LLM can respond
|
||||||
// authentically ("I seem to be starting fresh" vs "memory system may be down")
|
// authentically ("I seem to be starting fresh" vs "memory system may be down")
|
||||||
// rather than silently acting as if it has context it doesn't have.
|
// rather than silently acting as if it has context it doesn't have.
|
||||||
@@ -909,6 +834,29 @@ fn conv_history_load() -> String {
|
|||||||
return content
|
return content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// session_preload_bullets — render up to max_bullets nodes from a JSON array as
|
||||||
|
// bullet lines, truncating content at snip_len chars each.
|
||||||
|
fn session_preload_bullets(nodes: String, max_bullets: Int, snip_len: Int) -> String {
|
||||||
|
if str_eq(nodes, "") { return "" }
|
||||||
|
if str_eq(nodes, "[]") { return "" }
|
||||||
|
let total: Int = json_array_len(nodes)
|
||||||
|
let limit: Int = if max_bullets < total { max_bullets } else { total }
|
||||||
|
let bullets: String = ""
|
||||||
|
let i: Int = 0
|
||||||
|
while i < limit {
|
||||||
|
let node: String = json_array_get(nodes, i)
|
||||||
|
let content: String = json_get(node, "content")
|
||||||
|
let snip: String = if str_len(content) > snip_len { str_slice(content, 0, snip_len) } else { content }
|
||||||
|
let bullets = if str_eq(snip, "") {
|
||||||
|
bullets
|
||||||
|
} else {
|
||||||
|
if str_eq(bullets, "") { "- " + snip } else { bullets + "\n- " + snip }
|
||||||
|
}
|
||||||
|
let i = i + 1
|
||||||
|
}
|
||||||
|
return bullets
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_chat(body: String) -> String {
|
fn handle_chat(body: String) -> String {
|
||||||
let message: String = json_get(body, "message")
|
let message: String = json_get(body, "message")
|
||||||
if str_eq(message, "") {
|
if str_eq(message, "") {
|
||||||
@@ -994,9 +942,10 @@ fn handle_chat(body: String) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Issue 4 fix: engram_compile_multi adds entity + emotion fan-out seeds
|
let ctx: String = engram_compile(activation_seed)
|
||||||
let ctx: String = engram_compile_multi(activation_seed, message)
|
let system: String = affective_prefix + build_system_prompt(ctx, true)
|
||||||
let system: String = affective_prefix + build_system_prompt(ctx)
|
|
||||||
|
let seen_ids: String = state_get("engram_compile_seen_ids")
|
||||||
|
|
||||||
// Issue 9 fix: add project-specific and session-summary searches to session preload.
|
// Issue 9 fix: add project-specific and session-summary searches to session preload.
|
||||||
// Old hardcoded "user profile" and "in_progress active project" miss project-specific
|
// Old hardcoded "user profile" and "in_progress active project" miss project-specific
|
||||||
@@ -1026,21 +975,24 @@ fn handle_chat(body: String) -> String {
|
|||||||
let bullets: String = ""
|
let bullets: String = ""
|
||||||
let bullets = if pn > 0 {
|
let bullets = if pn > 0 {
|
||||||
let n0: String = json_array_get(profile_nodes, 0)
|
let n0: String = json_array_get(profile_nodes, 0)
|
||||||
|
let id0: String = json_get(n0, "id")
|
||||||
let c0: String = json_get(n0, "content")
|
let c0: String = json_get(n0, "content")
|
||||||
let s0: String = if str_len(c0) > 120 { str_slice(c0, 0, 120) } else { c0 }
|
let s0: String = if str_len(c0) > 120 { str_slice(c0, 0, 120) } else { c0 }
|
||||||
if str_eq(s0, "") { bullets } else { "- " + s0 }
|
if id_in_seen(id0, seen_ids) || str_eq(s0, "") { bullets } else { "- " + s0 }
|
||||||
} else { bullets }
|
} else { bullets }
|
||||||
let bullets = if pn > 1 {
|
let bullets = if pn > 1 {
|
||||||
let n1: String = json_array_get(profile_nodes, 1)
|
let n1: String = json_array_get(profile_nodes, 1)
|
||||||
|
let id1: String = json_get(n1, "id")
|
||||||
let c1: String = json_get(n1, "content")
|
let c1: String = json_get(n1, "content")
|
||||||
let s1: String = if str_len(c1) > 120 { str_slice(c1, 0, 120) } else { c1 }
|
let s1: String = if str_len(c1) > 120 { str_slice(c1, 0, 120) } else { c1 }
|
||||||
if str_eq(s1, "") { bullets } else { bullets + "\n- " + s1 }
|
if id_in_seen(id1, seen_ids) || str_eq(s1, "") { bullets } else { bullets + "\n- " + s1 }
|
||||||
} else { bullets }
|
} else { bullets }
|
||||||
let bullets = if pn > 2 {
|
let bullets = if pn > 2 {
|
||||||
let n2: String = json_array_get(profile_nodes, 2)
|
let n2: String = json_array_get(profile_nodes, 2)
|
||||||
|
let id2: String = json_get(n2, "id")
|
||||||
let c2: String = json_get(n2, "content")
|
let c2: String = json_get(n2, "content")
|
||||||
let s2: String = if str_len(c2) > 120 { str_slice(c2, 0, 120) } else { c2 }
|
let s2: String = if str_len(c2) > 120 { str_slice(c2, 0, 120) } else { c2 }
|
||||||
if str_eq(s2, "") { bullets } else { bullets + "\n- " + s2 }
|
if id_in_seen(id2, seen_ids) || str_eq(s2, "") { bullets } else { bullets + "\n- " + s2 }
|
||||||
} else { bullets }
|
} else { bullets }
|
||||||
bullets
|
bullets
|
||||||
} else { "" }
|
} else { "" }
|
||||||
@@ -1050,15 +1002,17 @@ fn handle_chat(body: String) -> String {
|
|||||||
let wb: String = ""
|
let wb: String = ""
|
||||||
let wb = if wn > 0 {
|
let wb = if wn > 0 {
|
||||||
let w0: String = json_array_get(work_nodes, 0)
|
let w0: String = json_array_get(work_nodes, 0)
|
||||||
|
let wid0: String = json_get(w0, "id")
|
||||||
let wc0: String = json_get(w0, "content")
|
let wc0: String = json_get(w0, "content")
|
||||||
let ws0: String = if str_len(wc0) > 120 { str_slice(wc0, 0, 120) } else { wc0 }
|
let ws0: String = if str_len(wc0) > 120 { str_slice(wc0, 0, 120) } else { wc0 }
|
||||||
if str_eq(ws0, "") { wb } else { "- " + ws0 }
|
if id_in_seen(wid0, seen_ids) || str_eq(ws0, "") { wb } else { "- " + ws0 }
|
||||||
} else { wb }
|
} else { wb }
|
||||||
let wb = if wn > 1 {
|
let wb = if wn > 1 {
|
||||||
let w1: String = json_array_get(work_nodes, 1)
|
let w1: String = json_array_get(work_nodes, 1)
|
||||||
|
let wid1: String = json_get(w1, "id")
|
||||||
let wc1: String = json_get(w1, "content")
|
let wc1: String = json_get(w1, "content")
|
||||||
let ws1: String = if str_len(wc1) > 120 { str_slice(wc1, 0, 120) } else { wc1 }
|
let ws1: String = if str_len(wc1) > 120 { str_slice(wc1, 0, 120) } else { wc1 }
|
||||||
if str_eq(ws1, "") { wb } else { wb + "\n- " + ws1 }
|
if id_in_seen(wid1, seen_ids) || str_eq(ws1, "") { wb } else { wb + "\n- " + ws1 }
|
||||||
} else { wb }
|
} else { wb }
|
||||||
wb
|
wb
|
||||||
} else { "" }
|
} else { "" }
|
||||||
@@ -1068,24 +1022,27 @@ fn handle_chat(body: String) -> String {
|
|||||||
let pb: String = ""
|
let pb: String = ""
|
||||||
let pb = if prn > 0 {
|
let pb = if prn > 0 {
|
||||||
let pr0: String = json_array_get(project_nodes, 0)
|
let pr0: String = json_array_get(project_nodes, 0)
|
||||||
|
let prid0: String = json_get(pr0, "id")
|
||||||
let prc0: String = json_get(pr0, "content")
|
let prc0: String = json_get(pr0, "content")
|
||||||
let ps0: String = if str_len(prc0) > 120 { str_slice(prc0, 0, 120) } else { prc0 }
|
let ps0: String = if str_len(prc0) > 120 { str_slice(prc0, 0, 120) } else { prc0 }
|
||||||
if str_eq(ps0, "") { pb } else { "- " + ps0 }
|
if id_in_seen(prid0, seen_ids) || str_eq(ps0, "") { pb } else { "- " + ps0 }
|
||||||
} else { pb }
|
} else { pb }
|
||||||
let pb = if prn > 1 {
|
let pb = if prn > 1 {
|
||||||
let pr1: String = json_array_get(project_nodes, 1)
|
let pr1: String = json_array_get(project_nodes, 1)
|
||||||
|
let prid1: String = json_get(pr1, "id")
|
||||||
let prc1: String = json_get(pr1, "content")
|
let prc1: String = json_get(pr1, "content")
|
||||||
let ps1: String = if str_len(prc1) > 120 { str_slice(prc1, 0, 120) } else { prc1 }
|
let ps1: String = if str_len(prc1) > 120 { str_slice(prc1, 0, 120) } else { prc1 }
|
||||||
if str_eq(ps1, "") { pb } else { pb + "\n- " + ps1 }
|
if id_in_seen(prid1, seen_ids) || str_eq(ps1, "") { pb } else { pb + "\n- " + ps1 }
|
||||||
} else { pb }
|
} else { pb }
|
||||||
pb
|
pb
|
||||||
} else { "" }
|
} else { "" }
|
||||||
|
|
||||||
let summary_bullet: String = if summary_ok {
|
let summary_bullet: String = if summary_ok {
|
||||||
let sn0: String = json_array_get(summary_nodes, 0)
|
let sn0: String = json_array_get(summary_nodes, 0)
|
||||||
|
let snid0: String = json_get(sn0, "id")
|
||||||
let sc0: String = json_get(sn0, "content")
|
let sc0: String = json_get(sn0, "content")
|
||||||
let ss0: String = if str_len(sc0) > 200 { str_slice(sc0, 0, 200) } else { sc0 }
|
let ss0: String = if str_len(sc0) > 200 { str_slice(sc0, 0, 200) } else { sc0 }
|
||||||
if str_eq(ss0, "") { "" } else { "- " + ss0 }
|
if id_in_seen(snid0, seen_ids) || str_eq(ss0, "") { "" } else { "- " + ss0 }
|
||||||
} else { "" }
|
} else { "" }
|
||||||
|
|
||||||
let hp: Bool = !str_eq(profile_bullets, "")
|
let hp: Bool = !str_eq(profile_bullets, "")
|
||||||
@@ -1643,7 +1600,7 @@ fn handle_chat_agentic(body: String) -> String {
|
|||||||
if str_eq(screen_action, "hard_bell") {
|
if str_eq(screen_action, "hard_bell") {
|
||||||
safety_log_bell("hard", json_get(screen_result, "reason"), str_slice(message, 0, 80))
|
safety_log_bell("hard", json_get(screen_result, "reason"), str_slice(message, 0, 80))
|
||||||
return "{\"reply\":\"" + json_safe(safety_validate("", "hard_bell")) + "\",\"model\":\"\",\"agentic\":true,\"tools_used\":[]}"
|
return "{\"reply\":\"" + json_safe(safety_validate("", "hard_bell")) + "\",\"model\":\"\",\"agentic\":true,\"tools_used\":[]}"
|
||||||
|
}
|
||||||
|
|
||||||
let req_model: String = json_get(body, "model")
|
let req_model: String = json_get(body, "model")
|
||||||
let model: String = if str_eq(req_model, "") { chat_default_model() } else { req_model }
|
let model: String = if str_eq(req_model, "") { chat_default_model() } else { req_model }
|
||||||
@@ -1731,12 +1688,25 @@ fn handle_chat_agentic(body: String) -> String {
|
|||||||
let safe_msg: String = json_safe(message)
|
let safe_msg: String = json_safe(message)
|
||||||
let safe_sys: String = json_safe(system)
|
let safe_sys: String = json_safe(system)
|
||||||
|
|
||||||
|
// Vision in the agentic brain (2026-06-27): when the client attaches an image
|
||||||
|
// (base64 in body "image", mime in "image_media_type"), send it as a real Anthropic
|
||||||
|
// image content block on THIS user turn — so the model sees raw pixels WITH memory,
|
||||||
|
// history, and tools (parity with the CLI). img_b64 == "" => byte-identical to before.
|
||||||
|
let img_b64: String = json_get(body, "image")
|
||||||
|
let img_mt_raw: String = json_get(body, "image_media_type")
|
||||||
|
let img_mt: String = if str_eq(img_mt_raw, "") { "image/png" } else { img_mt_raw }
|
||||||
|
let cur_user_content: String = if str_eq(img_b64, "") {
|
||||||
|
"\"" + safe_msg + "\""
|
||||||
|
} else {
|
||||||
|
"[{\"type\":\"text\",\"text\":\"" + safe_msg + "\"},{\"type\":\"image\",\"source\":{\"type\":\"base64\",\"media_type\":\"" + img_mt + "\",\"data\":\"" + img_b64 + "\"}}]"
|
||||||
|
}
|
||||||
|
|
||||||
// Seed the messages array with recent history if available, so the LLM sees the thread.
|
// Seed the messages array with recent history if available, so the LLM sees the thread.
|
||||||
let prior_messages: String = if agentic_hist_len > 0 {
|
let prior_messages: String = if agentic_hist_len > 0 {
|
||||||
let inner: String = str_slice(agentic_hist, 1, str_len(agentic_hist) - 1)
|
let inner: String = str_slice(agentic_hist, 1, str_len(agentic_hist) - 1)
|
||||||
"[" + inner + ",{\"role\":\"user\",\"content\":\"" + safe_msg + "\"}]"
|
"[" + inner + ",{\"role\":\"user\",\"content\":" + cur_user_content + "}]"
|
||||||
} else {
|
} else {
|
||||||
"[{\"role\":\"user\",\"content\":\"" + safe_msg + "\"}]"
|
"[{\"role\":\"user\",\"content\":" + cur_user_content + "}]"
|
||||||
}
|
}
|
||||||
let messages: String = prior_messages
|
let messages: String = prior_messages
|
||||||
let api_url: String = "https://api.anthropic.com/v1/messages"
|
let api_url: String = "https://api.anthropic.com/v1/messages"
|
||||||
|
|||||||
@@ -1,38 +1,58 @@
|
|||||||
// auto-generated by elc --emit-header - do not edit
|
// auto-generated by elc --emit-header — do not edit
|
||||||
extern fn chat_default_model() -> String
|
extern fn chat_default_model() -> String
|
||||||
extern fn gemini_api_key() -> String
|
extern fn engram_numeric_valid(s: String) -> Bool
|
||||||
extern fn xai_api_key() -> String
|
extern fn parse_float_x100(s: String) -> Int
|
||||||
extern fn llm_call_grok(model: String, system: String, message: String) -> String
|
extern fn engram_score_node(node_json: String) -> Int
|
||||||
extern fn llm_call_gemini(model: String, system: String, message: String) -> String
|
extern fn engram_render_node(node_json: String) -> String
|
||||||
extern fn build_identity_from_graph() -> String
|
extern fn engram_render_nodes(nodes_json: String) -> String
|
||||||
|
extern fn engram_dedup_nodes(nodes_json: String) -> String
|
||||||
|
extern fn engram_compile_ranked(nodes_json: String, max_nodes: Int) -> String
|
||||||
|
extern fn engram_split_topics(message: String) -> String
|
||||||
|
extern fn engram_extract_entities(message: String) -> String
|
||||||
|
extern fn engram_detect_recall_intent(message: String) -> Bool
|
||||||
|
extern fn engram_is_continuation(message: String, hist_len: Int) -> Bool
|
||||||
|
extern fn engram_compile_multi(topic: String) -> String
|
||||||
|
extern fn engram_nodes_merge(a: String, b: String) -> String
|
||||||
|
extern fn id_in_seen(node_id: String, seen: String) -> Bool
|
||||||
|
extern fn add_to_seen(seen: String, node_id: String) -> String
|
||||||
|
extern fn engram_extract_ids(nodes_json: String) -> String
|
||||||
extern fn engram_compile(intent: String) -> String
|
extern fn engram_compile(intent: String) -> String
|
||||||
extern fn json_safe(s: String) -> String
|
extern fn json_safe(s: String) -> String
|
||||||
extern fn build_system_prompt(ctx: String) -> String
|
extern fn build_system_prompt(ctx: String, chat_mode: Bool) -> String
|
||||||
extern fn hist_append(hist: String, role: String, content: String) -> String
|
extern fn hist_append(hist: String, role: String, content: String) -> String
|
||||||
extern fn hist_trim(hist: String) -> String
|
extern fn hist_trim(hist: String) -> String
|
||||||
|
extern fn hist_trim_with_bell_guard(hist: String) -> String
|
||||||
extern fn clean_llm_response(s: String) -> String
|
extern fn clean_llm_response(s: String) -> String
|
||||||
extern fn conv_history_persist(hist: String) -> Void
|
extern fn conv_history_persist(hist: String) -> Void
|
||||||
extern fn conv_history_load() -> String
|
extern fn conv_history_load() -> String
|
||||||
|
extern fn session_preload_bullets(nodes: String, max_bullets: Int, snip_len: Int) -> String
|
||||||
extern fn handle_chat(body: String) -> String
|
extern fn handle_chat(body: String) -> String
|
||||||
extern fn handle_see(body: String) -> String
|
extern fn handle_see(body: String) -> String
|
||||||
extern fn studio_tools_json() -> String
|
extern fn studio_tools_json() -> String
|
||||||
extern fn agentic_api_key() -> String
|
extern fn agentic_api_key() -> String
|
||||||
extern fn call_neuron_mcp(tool_name: String, args_json: String) -> String
|
|
||||||
extern fn agentic_tools_literal() -> String
|
extern fn agentic_tools_literal() -> String
|
||||||
extern fn agentic_tools_with_web() -> String
|
extern fn agentic_tools_with_web() -> String
|
||||||
|
extern fn connector_tools_json() -> String
|
||||||
|
extern fn agentic_tools_all() -> String
|
||||||
|
extern fn call_mcp_bridge(tool_name: String, tool_input: String) -> String
|
||||||
|
extern fn tool_auto_approved(tool_name: String) -> Bool
|
||||||
|
extern fn call_neuron_mcp(tool_name: String, args: String) -> String
|
||||||
|
extern fn agent_workspace_root() -> String
|
||||||
|
extern fn path_within_root(path: String, root: String) -> Bool
|
||||||
|
extern fn resolve_in_root(path: String, root: String) -> String
|
||||||
extern fn dispatch_tool(tool_name: String, tool_input: String) -> String
|
extern fn dispatch_tool(tool_name: String, tool_input: String) -> String
|
||||||
extern fn json_array_append(arr: String, item: String) -> String
|
extern fn is_builtin_tool(tool_name: String) -> Bool
|
||||||
extern fn append_tool_log(log: String, name: String) -> String
|
extern fn next_bridge_id() -> String
|
||||||
extern fn exec_tool_block(block: String) -> String
|
|
||||||
extern fn agentic_blob(model: String, system: String, tools_json: String, messages: String, origin: String, approval: Bool, iteration: Int, tools_log: String, content: String, queue: String, results: String, next: Int) -> String
|
|
||||||
extern fn extract_all_text(s: String) -> String
|
|
||||||
extern fn strip_citations(s: String) -> String
|
|
||||||
extern fn agentic_api_turn(model: String, safe_sys: String, tools_json: String, messages: String) -> String
|
|
||||||
extern fn agentic_engine(session_id: String, blob: String) -> String
|
|
||||||
extern fn handle_chat_agentic(body: String) -> String
|
extern fn handle_chat_agentic(body: String) -> String
|
||||||
extern fn handle_session_approve(session_id: String, body: String) -> String
|
extern fn agentic_loop(session_id: String, model: String, safe_sys: String, tools_json: String, messages_in: String, h: Map, tools_log_in: String) -> String
|
||||||
|
extern fn bridge_save(session_id: String, model: String, safe_sys: String, tools_json: String, messages: String, tools_log: String, tool_use_id: String) -> Bool
|
||||||
|
extern fn agentic_resume(session_id: String, tool_use_id: String, content: String) -> String
|
||||||
|
extern fn handle_tool_result(session_id: String, body: String) -> String
|
||||||
extern fn handle_chat_as_soul(body: String) -> String
|
extern fn handle_chat_as_soul(body: String) -> String
|
||||||
extern fn handle_dharma_room_turn(body: String) -> String
|
extern fn handle_dharma_room_turn(body: String) -> String
|
||||||
extern fn handle_dharma_room_turn_agentic(body: String) -> String
|
extern fn handle_dharma_room_turn_agentic(body: String) -> String
|
||||||
|
extern fn session_summary_write(summary_text: String) -> String
|
||||||
|
extern fn session_summary_write_dated(summary_text: String, label: String) -> String
|
||||||
|
extern fn session_summary_autogenerate(hist: String) -> String
|
||||||
extern fn auto_persist(req: String, resp: String) -> Void
|
extern fn auto_persist(req: String, resp: String) -> Void
|
||||||
extern fn strengthen_chat_nodes(activation_nodes: String) -> Void
|
extern fn strengthen_chat_nodes(activation_nodes: String) -> Void
|
||||||
|
|||||||
+46
-133
@@ -25,6 +25,7 @@ el_val_t elapsed_ms(void);
|
|||||||
el_val_t elapsed_human(void);
|
el_val_t elapsed_human(void);
|
||||||
el_val_t embed_ok(void);
|
el_val_t embed_ok(void);
|
||||||
el_val_t emit_heartbeat(void);
|
el_val_t emit_heartbeat(void);
|
||||||
|
el_val_t auto_term_try_slot(el_val_t slot_type, el_val_t slot_lbl);
|
||||||
el_val_t proactive_curiosity(void);
|
el_val_t proactive_curiosity(void);
|
||||||
el_val_t pulse_count(void);
|
el_val_t pulse_count(void);
|
||||||
el_val_t pulse_inc(void);
|
el_val_t pulse_inc(void);
|
||||||
@@ -42,110 +43,6 @@ el_val_t threat_score_history(el_val_t history);
|
|||||||
el_val_t threat_trajectory_check(el_val_t tool_name, el_val_t tool_input);
|
el_val_t threat_trajectory_check(el_val_t tool_name, el_val_t tool_input);
|
||||||
el_val_t threat_history_append(el_val_t text);
|
el_val_t threat_history_append(el_val_t text);
|
||||||
|
|
||||||
el_val_t tier_working(void) {
|
|
||||||
return EL_STR("Working");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t tier_episodic(void) {
|
|
||||||
return EL_STR("Episodic");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t tier_canonical(void) {
|
|
||||||
return EL_STR("Canonical");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_store(el_val_t content, el_val_t label, el_val_t tags) {
|
|
||||||
return engram_node_full(content, EL_STR("Memory"), label, el_from_float(el_from_float(0.5)), el_from_float(el_from_float(0.5)), el_from_float(el_from_float(0.8)), EL_STR("Working"), tags);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_remember(el_val_t content, el_val_t tags) {
|
|
||||||
return mem_store(content, EL_STR("soul-memory"), tags);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_recall(el_val_t query, el_val_t depth) {
|
|
||||||
return engram_activate_json(query, depth);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_search(el_val_t query, el_val_t limit) {
|
|
||||||
return engram_search_json(query, limit);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_strengthen(el_val_t node_id) {
|
|
||||||
engram_strengthen(node_id);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_forget(el_val_t node_id) {
|
|
||||||
engram_forget(node_id);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_consolidate(void) {
|
|
||||||
el_val_t scanned = engram_node_count();
|
|
||||||
el_val_t dummy = engram_scan_nodes_json(100, 0);
|
|
||||||
el_val_t total_nodes = engram_node_count();
|
|
||||||
el_val_t total_edges = engram_edge_count();
|
|
||||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"scanned\":"), int_to_str(scanned)), EL_STR(",\"total_nodes\":")), int_to_str(total_nodes)), EL_STR(",\"total_edges\":")), int_to_str(total_edges)), EL_STR("}"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_save(el_val_t path) {
|
|
||||||
engram_save(path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_load(el_val_t path) {
|
|
||||||
engram_load(path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_boot_count_get(void) {
|
|
||||||
el_val_t results = engram_search_json(EL_STR("soul:boot_count"), 3);
|
|
||||||
if (str_eq(results, EL_STR(""))) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (str_eq(results, EL_STR("[]"))) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
el_val_t node = json_array_get(results, 0);
|
|
||||||
el_val_t content = json_get(node, EL_STR("content"));
|
|
||||||
el_val_t prefix = EL_STR("soul:boot_count:");
|
|
||||||
if (!str_starts_with(content, prefix)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
el_val_t num_str = str_slice(content, str_len(prefix), str_len(content));
|
|
||||||
return str_to_int(num_str);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_boot_count_inc(void) {
|
|
||||||
el_val_t current = mem_boot_count_get();
|
|
||||||
el_val_t next = (current + 1);
|
|
||||||
el_val_t content = el_str_concat(EL_STR("soul:boot_count:"), int_to_str(next));
|
|
||||||
el_val_t tags = EL_STR("[\"soul-meta\",\"boot-counter\"]");
|
|
||||||
el_val_t discard = engram_node_full(content, EL_STR("Memory"), EL_STR("soul:boot_count"), el_from_float(el_from_float(0.9)), el_from_float(el_from_float(0.9)), el_from_float(el_from_float(1.0)), EL_STR("Canonical"), tags);
|
|
||||||
return next;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_emit_state_event(el_val_t trigger, el_val_t kind, el_val_t content) {
|
|
||||||
el_val_t boot = mem_boot_count_get();
|
|
||||||
el_val_t ts = time_now();
|
|
||||||
el_val_t safe_trigger = str_replace(trigger, EL_STR("\""), EL_STR("'"));
|
|
||||||
el_val_t safe_content = str_replace(content, EL_STR("\""), EL_STR("'"));
|
|
||||||
el_val_t payload = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"trigger\":\""), safe_trigger), EL_STR("\"")), EL_STR(",\"kind\":\"")), kind), EL_STR("\"")), EL_STR(",\"content\":\"")), safe_content), EL_STR("\"")), EL_STR(",\"boot\":")), int_to_str(boot)), EL_STR(",\"ts\":")), int_to_str(ts)), EL_STR("}"));
|
|
||||||
el_val_t tags = EL_STR("[\"internal-state\",\"pre-reasoning\",\"InternalStateEvent\"]");
|
|
||||||
return engram_node_full(payload, EL_STR("InternalStateEvent"), el_str_concat(EL_STR("state-event:"), kind), el_from_float(el_from_float(0.85)), el_from_float(el_from_float(0.8)), el_from_float(el_from_float(0.9)), EL_STR("Episodic"), tags);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t idle_count(void) {
|
el_val_t idle_count(void) {
|
||||||
el_val_t s = state_get(EL_STR("soul.idle"));
|
el_val_t s = state_get(EL_STR("soul.idle"));
|
||||||
if (str_eq(s, EL_STR(""))) {
|
if (str_eq(s, EL_STR(""))) {
|
||||||
@@ -171,7 +68,7 @@ el_val_t ise_post(el_val_t content) {
|
|||||||
el_val_t ise_url = env(EL_STR("SOUL_ISE_URL"));
|
el_val_t ise_url = env(EL_STR("SOUL_ISE_URL"));
|
||||||
el_val_t engram_url = ({ el_val_t _if_result_1 = 0; if (str_eq(ise_url, EL_STR(""))) { _if_result_1 = (state_get(EL_STR("soul_engram_url"))); } else { _if_result_1 = (ise_url); } _if_result_1; });
|
el_val_t engram_url = ({ el_val_t _if_result_1 = 0; if (str_eq(ise_url, EL_STR(""))) { _if_result_1 = (state_get(EL_STR("soul_engram_url"))); } else { _if_result_1 = (ise_url); } _if_result_1; });
|
||||||
if (str_eq(engram_url, EL_STR(""))) {
|
if (str_eq(engram_url, EL_STR(""))) {
|
||||||
el_val_t discard = engram_node_full(content, EL_STR("InternalStateEvent"), EL_STR("state-event"), el_from_float(el_from_float(0.3)), el_from_float(el_from_float(0.3)), el_from_float(el_from_float(0.8)), EL_STR("Episodic"), EL_STR("[\"internal-state\",\"InternalStateEvent\"]"));
|
el_val_t discard = engram_node_full(content, EL_STR("InternalStateEvent"), EL_STR("state-event"), el_from_float(0.3), el_from_float(0.3), el_from_float(0.8), EL_STR("Episodic"), EL_STR("[\"internal-state\",\"InternalStateEvent\"]"));
|
||||||
return EL_STR("");
|
return EL_STR("");
|
||||||
}
|
}
|
||||||
el_val_t safe1 = str_replace(content, EL_STR("\\"), EL_STR("\\\\"));
|
el_val_t safe1 = str_replace(content, EL_STR("\\"), EL_STR("\\\\"));
|
||||||
@@ -245,6 +142,29 @@ el_val_t emit_heartbeat(void) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
el_val_t auto_term_try_slot(el_val_t slot_type, el_val_t slot_lbl) {
|
||||||
|
state_set(EL_STR("_ats_ok"), EL_STR("0"));
|
||||||
|
if (str_eq(slot_type, EL_STR("Memory"))) {
|
||||||
|
state_set(EL_STR("_ats_ok"), EL_STR("1"));
|
||||||
|
}
|
||||||
|
if (str_eq(slot_type, EL_STR("BacklogItem"))) {
|
||||||
|
state_set(EL_STR("_ats_ok"), EL_STR("1"));
|
||||||
|
}
|
||||||
|
if (str_eq(slot_type, EL_STR("Entity"))) {
|
||||||
|
state_set(EL_STR("_ats_ok"), EL_STR("1"));
|
||||||
|
}
|
||||||
|
if (str_eq(state_get(EL_STR("_ats_ok")), EL_STR("1"))) {
|
||||||
|
if (!str_eq(slot_lbl, EL_STR(""))) {
|
||||||
|
el_val_t sp = str_find_chars(slot_lbl, EL_STR(" :(["));
|
||||||
|
if (sp > 3) {
|
||||||
|
state_set(EL_STR("cseed_auto"), str_slice(slot_lbl, 0, sp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return EL_STR("");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
el_val_t proactive_curiosity(void) {
|
el_val_t proactive_curiosity(void) {
|
||||||
el_val_t ts = time_now();
|
el_val_t ts = time_now();
|
||||||
el_val_t ts_minutes = (ts / 60000);
|
el_val_t ts_minutes = (ts / 60000);
|
||||||
@@ -282,29 +202,27 @@ el_val_t proactive_curiosity(void) {
|
|||||||
el_val_t found_c = json_array_len(results_c);
|
el_val_t found_c = json_array_len(results_c);
|
||||||
el_val_t found = ((found_a + found_b) + found_c);
|
el_val_t found = ((found_a + found_b) + found_c);
|
||||||
state_set(EL_STR("cseed_auto"), EL_STR(""));
|
state_set(EL_STR("cseed_auto"), EL_STR(""));
|
||||||
el_val_t wm_top_j = engram_wm_top_json(1);
|
el_val_t wm10 = engram_wm_top_json(10);
|
||||||
el_val_t wm_top_n = json_array_get(wm_top_j, 0);
|
el_val_t wm10_n9 = json_array_get(wm10, 9);
|
||||||
el_val_t wm_top_lbl = json_get(wm_top_n, EL_STR("label"));
|
el_val_t wm10_n8 = json_array_get(wm10, 8);
|
||||||
el_val_t wm_top_type = json_get(wm_top_n, EL_STR("node_type"));
|
el_val_t wm10_n7 = json_array_get(wm10, 7);
|
||||||
state_set(EL_STR("allow_auto"), EL_STR("0"));
|
el_val_t wm10_n6 = json_array_get(wm10, 6);
|
||||||
if (str_eq(wm_top_type, EL_STR("Memory"))) {
|
el_val_t wm10_n5 = json_array_get(wm10, 5);
|
||||||
state_set(EL_STR("allow_auto"), EL_STR("1"));
|
el_val_t wm10_n4 = json_array_get(wm10, 4);
|
||||||
}
|
el_val_t wm10_n3 = json_array_get(wm10, 3);
|
||||||
if (str_eq(wm_top_type, EL_STR("BacklogItem"))) {
|
el_val_t wm10_n2 = json_array_get(wm10, 2);
|
||||||
state_set(EL_STR("allow_auto"), EL_STR("1"));
|
el_val_t wm10_n1 = json_array_get(wm10, 1);
|
||||||
}
|
el_val_t wm10_n0 = json_array_get(wm10, 0);
|
||||||
if (str_eq(wm_top_type, EL_STR("Entity"))) {
|
auto_term_try_slot(json_get(wm10_n9, EL_STR("node_type")), json_get(wm10_n9, EL_STR("label")));
|
||||||
state_set(EL_STR("allow_auto"), EL_STR("1"));
|
auto_term_try_slot(json_get(wm10_n8, EL_STR("node_type")), json_get(wm10_n8, EL_STR("label")));
|
||||||
}
|
auto_term_try_slot(json_get(wm10_n7, EL_STR("node_type")), json_get(wm10_n7, EL_STR("label")));
|
||||||
el_val_t allow_auto = state_get(EL_STR("allow_auto"));
|
auto_term_try_slot(json_get(wm10_n6, EL_STR("node_type")), json_get(wm10_n6, EL_STR("label")));
|
||||||
if (str_eq(allow_auto, EL_STR("1"))) {
|
auto_term_try_slot(json_get(wm10_n5, EL_STR("node_type")), json_get(wm10_n5, EL_STR("label")));
|
||||||
if (!str_eq(wm_top_lbl, EL_STR(""))) {
|
auto_term_try_slot(json_get(wm10_n4, EL_STR("node_type")), json_get(wm10_n4, EL_STR("label")));
|
||||||
el_val_t sp = str_find_chars(wm_top_lbl, EL_STR(" :(["));
|
auto_term_try_slot(json_get(wm10_n3, EL_STR("node_type")), json_get(wm10_n3, EL_STR("label")));
|
||||||
if (sp > 3) {
|
auto_term_try_slot(json_get(wm10_n2, EL_STR("node_type")), json_get(wm10_n2, EL_STR("label")));
|
||||||
state_set(EL_STR("cseed_auto"), str_slice(wm_top_lbl, 0, sp));
|
auto_term_try_slot(json_get(wm10_n1, EL_STR("node_type")), json_get(wm10_n1, EL_STR("label")));
|
||||||
}
|
auto_term_try_slot(json_get(wm10_n0, EL_STR("node_type")), json_get(wm10_n0, EL_STR("label")));
|
||||||
}
|
|
||||||
}
|
|
||||||
el_val_t auto_term = state_get(EL_STR("cseed_auto"));
|
el_val_t auto_term = state_get(EL_STR("cseed_auto"));
|
||||||
el_val_t results_auto = ({ el_val_t _if_result_3 = 0; if (str_eq(auto_term, EL_STR(""))) { _if_result_3 = (EL_STR("[]")); } else { _if_result_3 = (engram_activate_json(auto_term, 1)); } _if_result_3; });
|
el_val_t results_auto = ({ el_val_t _if_result_3 = 0; if (str_eq(auto_term, EL_STR(""))) { _if_result_3 = (EL_STR("[]")); } else { _if_result_3 = (engram_activate_json(auto_term, 1)); } _if_result_3; });
|
||||||
el_val_t found_auto = json_array_len(results_auto);
|
el_val_t found_auto = json_array_len(results_auto);
|
||||||
@@ -658,8 +576,3 @@ el_val_t threat_history_append(el_val_t text) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int _argc, char** _argv) {
|
|
||||||
el_runtime_init_args(_argc, _argv);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
+914
-289
File diff suppressed because one or more lines are too long
+37
-8
@@ -1,29 +1,58 @@
|
|||||||
// auto-generated by elc --emit-header - do not edit
|
// auto-generated by elc --emit-header — do not edit
|
||||||
extern fn chat_default_model() -> String
|
extern fn chat_default_model() -> String
|
||||||
extern fn gemini_api_key() -> String
|
extern fn engram_numeric_valid(s: String) -> Bool
|
||||||
extern fn xai_api_key() -> String
|
extern fn parse_float_x100(s: String) -> Int
|
||||||
extern fn llm_call_grok(model: String, system: String, message: String) -> String
|
extern fn engram_score_node(node_json: String) -> Int
|
||||||
extern fn llm_call_gemini(model: String, system: String, message: String) -> String
|
extern fn engram_render_node(node_json: String) -> String
|
||||||
extern fn build_identity_from_graph() -> String
|
extern fn engram_render_nodes(nodes_json: String) -> String
|
||||||
|
extern fn engram_dedup_nodes(nodes_json: String) -> String
|
||||||
|
extern fn engram_compile_ranked(nodes_json: String, max_nodes: Int) -> String
|
||||||
|
extern fn engram_split_topics(message: String) -> String
|
||||||
|
extern fn engram_extract_entities(message: String) -> String
|
||||||
|
extern fn engram_detect_recall_intent(message: String) -> Bool
|
||||||
|
extern fn engram_is_continuation(message: String, hist_len: Int) -> Bool
|
||||||
|
extern fn engram_compile_multi(topic: String) -> String
|
||||||
|
extern fn engram_nodes_merge(a: String, b: String) -> String
|
||||||
|
extern fn id_in_seen(node_id: String, seen: String) -> Bool
|
||||||
|
extern fn add_to_seen(seen: String, node_id: String) -> String
|
||||||
|
extern fn engram_extract_ids(nodes_json: String) -> String
|
||||||
extern fn engram_compile(intent: String) -> String
|
extern fn engram_compile(intent: String) -> String
|
||||||
extern fn json_safe(s: String) -> String
|
extern fn json_safe(s: String) -> String
|
||||||
extern fn build_system_prompt(ctx: String) -> String
|
extern fn build_system_prompt(ctx: String, chat_mode: Bool) -> String
|
||||||
extern fn hist_append(hist: String, role: String, content: String) -> String
|
extern fn hist_append(hist: String, role: String, content: String) -> String
|
||||||
extern fn hist_trim(hist: String) -> String
|
extern fn hist_trim(hist: String) -> String
|
||||||
|
extern fn hist_trim_with_bell_guard(hist: String) -> String
|
||||||
extern fn clean_llm_response(s: String) -> String
|
extern fn clean_llm_response(s: String) -> String
|
||||||
extern fn conv_history_persist(hist: String) -> Void
|
extern fn conv_history_persist(hist: String) -> Void
|
||||||
extern fn conv_history_load() -> String
|
extern fn conv_history_load() -> String
|
||||||
|
extern fn session_preload_bullets(nodes: String, max_bullets: Int, snip_len: Int) -> String
|
||||||
extern fn handle_chat(body: String) -> String
|
extern fn handle_chat(body: String) -> String
|
||||||
extern fn handle_see(body: String) -> String
|
extern fn handle_see(body: String) -> String
|
||||||
extern fn studio_tools_json() -> String
|
extern fn studio_tools_json() -> String
|
||||||
extern fn agentic_api_key() -> String
|
extern fn agentic_api_key() -> String
|
||||||
extern fn call_neuron_mcp(tool_name: String, args_json: String) -> String
|
|
||||||
extern fn agentic_tools_literal() -> String
|
extern fn agentic_tools_literal() -> String
|
||||||
extern fn agentic_tools_with_web() -> String
|
extern fn agentic_tools_with_web() -> String
|
||||||
|
extern fn connector_tools_json() -> String
|
||||||
|
extern fn agentic_tools_all() -> String
|
||||||
|
extern fn call_mcp_bridge(tool_name: String, tool_input: String) -> String
|
||||||
|
extern fn tool_auto_approved(tool_name: String) -> Bool
|
||||||
|
extern fn call_neuron_mcp(tool_name: String, args: String) -> String
|
||||||
|
extern fn agent_workspace_root() -> String
|
||||||
|
extern fn path_within_root(path: String, root: String) -> Bool
|
||||||
|
extern fn resolve_in_root(path: String, root: String) -> String
|
||||||
extern fn dispatch_tool(tool_name: String, tool_input: String) -> String
|
extern fn dispatch_tool(tool_name: String, tool_input: String) -> String
|
||||||
|
extern fn is_builtin_tool(tool_name: String) -> Bool
|
||||||
|
extern fn next_bridge_id() -> String
|
||||||
extern fn handle_chat_agentic(body: String) -> String
|
extern fn handle_chat_agentic(body: String) -> String
|
||||||
|
extern fn agentic_loop(session_id: String, model: String, safe_sys: String, tools_json: String, messages_in: String, h: Map, tools_log_in: String) -> String
|
||||||
|
extern fn bridge_save(session_id: String, model: String, safe_sys: String, tools_json: String, messages: String, tools_log: String, tool_use_id: String) -> Bool
|
||||||
|
extern fn agentic_resume(session_id: String, tool_use_id: String, content: String) -> String
|
||||||
|
extern fn handle_tool_result(session_id: String, body: String) -> String
|
||||||
extern fn handle_chat_as_soul(body: String) -> String
|
extern fn handle_chat_as_soul(body: String) -> String
|
||||||
extern fn handle_dharma_room_turn(body: String) -> String
|
extern fn handle_dharma_room_turn(body: String) -> String
|
||||||
extern fn handle_dharma_room_turn_agentic(body: String) -> String
|
extern fn handle_dharma_room_turn_agentic(body: String) -> String
|
||||||
|
extern fn session_summary_write(summary_text: String) -> String
|
||||||
|
extern fn session_summary_write_dated(summary_text: String, label: String) -> String
|
||||||
|
extern fn session_summary_autogenerate(hist: String) -> String
|
||||||
extern fn auto_persist(req: String, resp: String) -> Void
|
extern fn auto_persist(req: String, resp: String) -> Void
|
||||||
extern fn strengthen_chat_nodes(activation_nodes: String) -> Void
|
extern fn strengthen_chat_nodes(activation_nodes: String) -> Void
|
||||||
|
|||||||
+72
@@ -2,9 +2,18 @@
|
|||||||
#include "el_runtime.h"
|
#include "el_runtime.h"
|
||||||
|
|
||||||
el_val_t add_punct(el_val_t s, el_val_t intent);
|
el_val_t add_punct(el_val_t s, el_val_t intent);
|
||||||
|
el_val_t add_to_seen(el_val_t seen, el_val_t node_id);
|
||||||
|
el_val_t aff_try_slot(el_val_t slot_json, el_val_t aff_7d_ts, el_val_t acc_key);
|
||||||
el_val_t agent_number(el_val_t agent);
|
el_val_t agent_number(el_val_t agent);
|
||||||
el_val_t agent_person(el_val_t agent);
|
el_val_t agent_person(el_val_t agent);
|
||||||
|
el_val_t agent_workspace_root(void);
|
||||||
el_val_t agentic_api_key(void);
|
el_val_t agentic_api_key(void);
|
||||||
|
el_val_t agentic_api_turn(el_val_t model, el_val_t safe_sys, el_val_t tools_json, el_val_t messages);
|
||||||
|
el_val_t agentic_blob(el_val_t model, el_val_t system, el_val_t tools_json, el_val_t messages, el_val_t origin, el_val_t approval, el_val_t iteration, el_val_t tools_log, el_val_t content, el_val_t queue, el_val_t results, el_val_t next);
|
||||||
|
el_val_t agentic_engine(el_val_t session_id, el_val_t blob);
|
||||||
|
el_val_t agentic_loop(el_val_t session_id, el_val_t model, el_val_t safe_sys, el_val_t tools_json, el_val_t messages_in, el_val_t h, el_val_t tools_log_in);
|
||||||
|
el_val_t agentic_resume(el_val_t session_id, el_val_t tool_use_id, el_val_t content);
|
||||||
|
el_val_t agentic_tools_all(void);
|
||||||
el_val_t agentic_tools_literal(void);
|
el_val_t agentic_tools_literal(void);
|
||||||
el_val_t agentic_tools_with_web(void);
|
el_val_t agentic_tools_with_web(void);
|
||||||
el_val_t agree_determiner(el_val_t det, el_val_t noun);
|
el_val_t agree_determiner(el_val_t det, el_val_t noun);
|
||||||
@@ -85,10 +94,13 @@ el_val_t api_err(el_val_t msg);
|
|||||||
el_val_t api_err_protected(el_val_t id);
|
el_val_t api_err_protected(el_val_t id);
|
||||||
el_val_t api_json_escape(el_val_t s);
|
el_val_t api_json_escape(el_val_t s);
|
||||||
el_val_t api_nonempty(el_val_t s);
|
el_val_t api_nonempty(el_val_t s);
|
||||||
|
el_val_t api_not_persisted(el_val_t id);
|
||||||
el_val_t api_ok(el_val_t extra);
|
el_val_t api_ok(el_val_t extra);
|
||||||
el_val_t api_or_empty(el_val_t s);
|
el_val_t api_or_empty(el_val_t s);
|
||||||
|
el_val_t api_persisted(el_val_t id);
|
||||||
el_val_t api_query_int(el_val_t path, el_val_t key, el_val_t default_val);
|
el_val_t api_query_int(el_val_t path, el_val_t key, el_val_t default_val);
|
||||||
el_val_t api_query_param(el_val_t path, el_val_t key);
|
el_val_t api_query_param(el_val_t path, el_val_t key);
|
||||||
|
el_val_t append_tool_log(el_val_t log, el_val_t name);
|
||||||
el_val_t ar_case_ending(el_val_t kase, el_val_t definite);
|
el_val_t ar_case_ending(el_val_t kase, el_val_t definite);
|
||||||
el_val_t ar_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t gender, el_val_t number);
|
el_val_t ar_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t gender, el_val_t number);
|
||||||
el_val_t ar_conjugate_form1(el_val_t past_base, el_val_t present_stem, el_val_t tense, el_val_t slot);
|
el_val_t ar_conjugate_form1(el_val_t past_base, el_val_t present_stem, el_val_t tense, el_val_t slot);
|
||||||
@@ -118,22 +130,28 @@ el_val_t ar_verb_form(el_val_t verb, el_val_t tense, el_val_t person, el_val_t n
|
|||||||
el_val_t attend(el_val_t node_json);
|
el_val_t attend(el_val_t node_json);
|
||||||
el_val_t auth_headers(el_val_t tok);
|
el_val_t auth_headers(el_val_t tok);
|
||||||
el_val_t auto_persist(el_val_t req, el_val_t resp);
|
el_val_t auto_persist(el_val_t req, el_val_t resp);
|
||||||
|
el_val_t auto_term_try_slot(el_val_t slot_type, el_val_t slot_lbl);
|
||||||
el_val_t awareness_run(void);
|
el_val_t awareness_run(void);
|
||||||
el_val_t axon_get(el_val_t path);
|
el_val_t axon_get(el_val_t path);
|
||||||
el_val_t axon_post(el_val_t path, el_val_t body);
|
el_val_t axon_post(el_val_t path, el_val_t body);
|
||||||
|
el_val_t bridge_save(el_val_t session_id, el_val_t model, el_val_t safe_sys, el_val_t tools_json, el_val_t messages, el_val_t tools_log, el_val_t tool_use_id);
|
||||||
el_val_t build_form_from_json(el_val_t semantic_form_json, el_val_t lang_code);
|
el_val_t build_form_from_json(el_val_t semantic_form_json, el_val_t lang_code);
|
||||||
el_val_t build_identity_from_graph(void);
|
el_val_t build_identity_from_graph(void);
|
||||||
el_val_t build_np(el_val_t referent, el_val_t slots);
|
el_val_t build_np(el_val_t referent, el_val_t slots);
|
||||||
el_val_t build_pp(el_val_t loc);
|
el_val_t build_pp(el_val_t loc);
|
||||||
el_val_t build_rules(void);
|
el_val_t build_rules(void);
|
||||||
el_val_t build_system_prompt(el_val_t ctx);
|
el_val_t build_system_prompt(el_val_t ctx);
|
||||||
|
el_val_t build_system_prompt(el_val_t ctx, el_val_t chat_mode);
|
||||||
el_val_t build_vocab(void);
|
el_val_t build_vocab(void);
|
||||||
el_val_t build_vp_body(el_val_t slots);
|
el_val_t build_vp_body(el_val_t slots);
|
||||||
el_val_t build_vp_from_slots(el_val_t slots);
|
el_val_t build_vp_from_slots(el_val_t slots);
|
||||||
|
el_val_t call_mcp_bridge(el_val_t tool_name, el_val_t tool_input);
|
||||||
|
el_val_t call_neuron_mcp(el_val_t tool_name, el_val_t args);
|
||||||
el_val_t call_neuron_mcp(el_val_t tool_name, el_val_t args_json);
|
el_val_t call_neuron_mcp(el_val_t tool_name, el_val_t args_json);
|
||||||
el_val_t capitalize_first(el_val_t s);
|
el_val_t capitalize_first(el_val_t s);
|
||||||
el_val_t chat_default_model(void);
|
el_val_t chat_default_model(void);
|
||||||
el_val_t clean_llm_response(el_val_t s);
|
el_val_t clean_llm_response(el_val_t s);
|
||||||
|
el_val_t connector_tools_json(void);
|
||||||
el_val_t conv_history_load(void);
|
el_val_t conv_history_load(void);
|
||||||
el_val_t conv_history_persist(el_val_t hist);
|
el_val_t conv_history_persist(el_val_t hist);
|
||||||
el_val_t cop_article(el_val_t gender, el_val_t number, el_val_t definite);
|
el_val_t cop_article(el_val_t gender, el_val_t number, el_val_t definite);
|
||||||
@@ -240,6 +258,19 @@ el_val_t en_verb_form(el_val_t base, el_val_t tense, el_val_t person, el_val_t n
|
|||||||
el_val_t en_verb_gerund(el_val_t base);
|
el_val_t en_verb_gerund(el_val_t base);
|
||||||
el_val_t en_verb_past(el_val_t base);
|
el_val_t en_verb_past(el_val_t base);
|
||||||
el_val_t engram_compile(el_val_t intent);
|
el_val_t engram_compile(el_val_t intent);
|
||||||
|
el_val_t engram_compile_multi(el_val_t topic);
|
||||||
|
el_val_t engram_compile_ranked(el_val_t nodes_json, el_val_t max_nodes);
|
||||||
|
el_val_t engram_dedup_nodes(el_val_t nodes_json);
|
||||||
|
el_val_t engram_detect_recall_intent(el_val_t message);
|
||||||
|
el_val_t engram_extract_entities(el_val_t message);
|
||||||
|
el_val_t engram_extract_ids(el_val_t nodes_json);
|
||||||
|
el_val_t engram_is_continuation(el_val_t message, el_val_t hist_len);
|
||||||
|
el_val_t engram_nodes_merge(el_val_t a, el_val_t b);
|
||||||
|
el_val_t engram_numeric_valid(el_val_t s);
|
||||||
|
el_val_t engram_render_node(el_val_t node_json);
|
||||||
|
el_val_t engram_render_nodes(el_val_t nodes_json);
|
||||||
|
el_val_t engram_score_node(el_val_t node_json);
|
||||||
|
el_val_t engram_split_topics(el_val_t message);
|
||||||
el_val_t enm_been_past(el_val_t slot);
|
el_val_t enm_been_past(el_val_t slot);
|
||||||
el_val_t enm_been_present(el_val_t slot);
|
el_val_t enm_been_present(el_val_t slot);
|
||||||
el_val_t enm_comen_past(el_val_t slot);
|
el_val_t enm_comen_past(el_val_t slot);
|
||||||
@@ -269,6 +300,7 @@ el_val_t enm_str_ends(el_val_t s, el_val_t suf);
|
|||||||
el_val_t enm_weak_past(el_val_t stem, el_val_t slot);
|
el_val_t enm_weak_past(el_val_t stem, el_val_t slot);
|
||||||
el_val_t enm_weak_present(el_val_t stem, el_val_t slot);
|
el_val_t enm_weak_present(el_val_t stem, el_val_t slot);
|
||||||
el_val_t enm_weak_stem(el_val_t verb);
|
el_val_t enm_weak_stem(el_val_t verb);
|
||||||
|
el_val_t ensure_self_canonical_bridge(void);
|
||||||
el_val_t entry_form(el_val_t entry, el_val_t n);
|
el_val_t entry_form(el_val_t entry, el_val_t n);
|
||||||
el_val_t entry_found(el_val_t entry);
|
el_val_t entry_found(el_val_t entry);
|
||||||
el_val_t entry_pos(el_val_t entry);
|
el_val_t entry_pos(el_val_t entry);
|
||||||
@@ -297,6 +329,8 @@ el_val_t es_str_last2(el_val_t s);
|
|||||||
el_val_t es_str_last3(el_val_t s);
|
el_val_t es_str_last3(el_val_t s);
|
||||||
el_val_t es_str_last_char(el_val_t s);
|
el_val_t es_str_last_char(el_val_t s);
|
||||||
el_val_t es_verb_class(el_val_t base);
|
el_val_t es_verb_class(el_val_t base);
|
||||||
|
el_val_t exec_tool_block(el_val_t block);
|
||||||
|
el_val_t extract_all_text(el_val_t s);
|
||||||
el_val_t extract_dim(el_val_t content, el_val_t key);
|
el_val_t extract_dim(el_val_t content, el_val_t key);
|
||||||
el_val_t fi_apply_case(el_val_t noun, el_val_t gram_case, el_val_t number);
|
el_val_t fi_apply_case(el_val_t noun, el_val_t gram_case, el_val_t number);
|
||||||
el_val_t fi_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
|
el_val_t fi_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
|
||||||
@@ -315,6 +349,7 @@ el_val_t fi_str_last_char(el_val_t s);
|
|||||||
el_val_t fi_suffix(el_val_t base, el_val_t harmony);
|
el_val_t fi_suffix(el_val_t base, el_val_t harmony);
|
||||||
el_val_t fi_verb_stem(el_val_t dict_form);
|
el_val_t fi_verb_stem(el_val_t dict_form);
|
||||||
el_val_t find_rule(el_val_t rule_id_str);
|
el_val_t find_rule(el_val_t rule_id_str);
|
||||||
|
el_val_t flag_true(el_val_t body, el_val_t key);
|
||||||
el_val_t fr_agree_article(el_val_t noun, el_val_t definite, el_val_t number);
|
el_val_t fr_agree_article(el_val_t noun, el_val_t definite, el_val_t number);
|
||||||
el_val_t fr_avoir_present(el_val_t slot);
|
el_val_t fr_avoir_present(el_val_t slot);
|
||||||
el_val_t fr_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
|
el_val_t fr_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
|
||||||
@@ -549,6 +584,9 @@ el_val_t handle_api_list_typed(el_val_t node_type, el_val_t path, el_val_t body)
|
|||||||
el_val_t handle_api_log_state_event(el_val_t body);
|
el_val_t handle_api_log_state_event(el_val_t body);
|
||||||
el_val_t handle_api_memory_delete(el_val_t body);
|
el_val_t handle_api_memory_delete(el_val_t body);
|
||||||
el_val_t handle_api_memory_update(el_val_t body);
|
el_val_t handle_api_memory_update(el_val_t body);
|
||||||
|
el_val_t handle_api_node_create(el_val_t body);
|
||||||
|
el_val_t handle_api_node_delete(el_val_t body);
|
||||||
|
el_val_t handle_api_node_update(el_val_t body);
|
||||||
el_val_t handle_api_promote_knowledge(el_val_t body);
|
el_val_t handle_api_promote_knowledge(el_val_t body);
|
||||||
el_val_t handle_api_recall(el_val_t method, el_val_t path, el_val_t body);
|
el_val_t handle_api_recall(el_val_t method, el_val_t path, el_val_t body);
|
||||||
el_val_t handle_api_remember(el_val_t body);
|
el_val_t handle_api_remember(el_val_t body);
|
||||||
@@ -566,9 +604,12 @@ el_val_t handle_dharma_room_turn_agentic(el_val_t body);
|
|||||||
el_val_t handle_elp_chat(el_val_t body);
|
el_val_t handle_elp_chat(el_val_t body);
|
||||||
el_val_t handle_nlg(el_val_t path, el_val_t method, el_val_t body);
|
el_val_t handle_nlg(el_val_t path, el_val_t method, el_val_t body);
|
||||||
el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body);
|
el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body);
|
||||||
|
el_val_t handle_safety_contact_get(void);
|
||||||
|
el_val_t handle_safety_contact_post(el_val_t body);
|
||||||
el_val_t handle_see(el_val_t body);
|
el_val_t handle_see(el_val_t body);
|
||||||
el_val_t handle_session_approve(el_val_t session_id, el_val_t body);
|
el_val_t handle_session_approve(el_val_t session_id, el_val_t body);
|
||||||
el_val_t handle_tool(el_val_t path, el_val_t method, el_val_t body);
|
el_val_t handle_tool(el_val_t path, el_val_t method, el_val_t body);
|
||||||
|
el_val_t handle_tool_result(el_val_t session_id, el_val_t body);
|
||||||
el_val_t hard_bell_threshold(void);
|
el_val_t hard_bell_threshold(void);
|
||||||
el_val_t he_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t gender, el_val_t number);
|
el_val_t he_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t gender, el_val_t number);
|
||||||
el_val_t he_conjugate_copula(el_val_t tense, el_val_t slot);
|
el_val_t he_conjugate_copula(el_val_t tense, el_val_t slot);
|
||||||
@@ -627,6 +668,8 @@ el_val_t hi_verb_stem(el_val_t infinitive);
|
|||||||
el_val_t hi_verb_stem_clean(el_val_t infinitive);
|
el_val_t hi_verb_stem_clean(el_val_t infinitive);
|
||||||
el_val_t hist_append(el_val_t hist, el_val_t role, el_val_t content);
|
el_val_t hist_append(el_val_t hist, el_val_t role, el_val_t content);
|
||||||
el_val_t hist_trim(el_val_t hist);
|
el_val_t hist_trim(el_val_t hist);
|
||||||
|
el_val_t hist_trim_with_bell_guard(el_val_t hist);
|
||||||
|
el_val_t id_in_seen(el_val_t node_id, el_val_t seen);
|
||||||
el_val_t idle_count(void);
|
el_val_t idle_count(void);
|
||||||
el_val_t idle_inc(void);
|
el_val_t idle_inc(void);
|
||||||
el_val_t idle_reset(void);
|
el_val_t idle_reset(void);
|
||||||
@@ -639,6 +682,7 @@ el_val_t imprint_unload(void);
|
|||||||
el_val_t init_soul_edges(void);
|
el_val_t init_soul_edges(void);
|
||||||
el_val_t irregular_plural(el_val_t word);
|
el_val_t irregular_plural(el_val_t word);
|
||||||
el_val_t irregular_singular(el_val_t word);
|
el_val_t irregular_singular(el_val_t word);
|
||||||
|
el_val_t is_builtin_tool(el_val_t tool_name);
|
||||||
el_val_t is_pronoun(el_val_t word);
|
el_val_t is_pronoun(el_val_t word);
|
||||||
el_val_t is_protected_node(el_val_t id);
|
el_val_t is_protected_node(el_val_t id);
|
||||||
el_val_t is_vowel(el_val_t c);
|
el_val_t is_vowel(el_val_t c);
|
||||||
@@ -651,6 +695,7 @@ el_val_t ja_noun_phrase(el_val_t noun, el_val_t gram_case);
|
|||||||
el_val_t ja_particle(el_val_t gram_case);
|
el_val_t ja_particle(el_val_t gram_case);
|
||||||
el_val_t ja_question_particle(void);
|
el_val_t ja_question_particle(void);
|
||||||
el_val_t ja_verb_group(el_val_t dict_form);
|
el_val_t ja_verb_group(el_val_t dict_form);
|
||||||
|
el_val_t json_array_append(el_val_t arr, el_val_t item);
|
||||||
el_val_t json_safe(el_val_t s);
|
el_val_t json_safe(el_val_t s);
|
||||||
el_val_t la_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
|
el_val_t la_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
|
||||||
el_val_t la_declension(el_val_t noun);
|
el_val_t la_declension(el_val_t noun);
|
||||||
@@ -737,6 +782,7 @@ el_val_t lang_profile_txb(void);
|
|||||||
el_val_t lang_profile_uga(void);
|
el_val_t lang_profile_uga(void);
|
||||||
el_val_t lang_profile_zh(void);
|
el_val_t lang_profile_zh(void);
|
||||||
el_val_t lang_word_order(el_val_t profile);
|
el_val_t lang_word_order(el_val_t profile);
|
||||||
|
el_val_t layered_cycle(el_val_t raw_input);
|
||||||
el_val_t lex_class(el_val_t entry);
|
el_val_t lex_class(el_val_t entry);
|
||||||
el_val_t lex_form(el_val_t entry, el_val_t idx);
|
el_val_t lex_form(el_val_t entry, el_val_t idx);
|
||||||
el_val_t lex_pos(el_val_t entry);
|
el_val_t lex_pos(el_val_t entry);
|
||||||
@@ -780,6 +826,7 @@ el_val_t morph_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_
|
|||||||
el_val_t morph_inflect(el_val_t word, el_val_t features, el_val_t profile);
|
el_val_t morph_inflect(el_val_t word, el_val_t features, el_val_t profile);
|
||||||
el_val_t morph_map_canonical(el_val_t verb, el_val_t code);
|
el_val_t morph_map_canonical(el_val_t verb, el_val_t code);
|
||||||
el_val_t morph_pluralize(el_val_t noun, el_val_t profile);
|
el_val_t morph_pluralize(el_val_t noun, el_val_t profile);
|
||||||
|
el_val_t next_bridge_id(void);
|
||||||
el_val_t nlg_is_ws(el_val_t c);
|
el_val_t nlg_is_ws(el_val_t c);
|
||||||
el_val_t non_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
|
el_val_t non_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
|
||||||
el_val_t non_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
|
el_val_t non_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
|
||||||
@@ -811,8 +858,10 @@ el_val_t non_vera_present(el_val_t slot);
|
|||||||
el_val_t non_weak_past(el_val_t stem, el_val_t slot);
|
el_val_t non_weak_past(el_val_t stem, el_val_t slot);
|
||||||
el_val_t non_weak_present(el_val_t stem, el_val_t slot);
|
el_val_t non_weak_present(el_val_t stem, el_val_t slot);
|
||||||
el_val_t one_cycle(void);
|
el_val_t one_cycle(void);
|
||||||
|
el_val_t parse_float_x100(el_val_t s);
|
||||||
el_val_t parse_session_id_from_path(el_val_t path);
|
el_val_t parse_session_id_from_path(el_val_t path);
|
||||||
el_val_t parse_session_subpath(el_val_t path);
|
el_val_t parse_session_subpath(el_val_t path);
|
||||||
|
el_val_t path_within_root(el_val_t path, el_val_t root);
|
||||||
el_val_t peo_ah_past(el_val_t slot);
|
el_val_t peo_ah_past(el_val_t slot);
|
||||||
el_val_t peo_ah_present(el_val_t slot);
|
el_val_t peo_ah_present(el_val_t slot);
|
||||||
el_val_t peo_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
|
el_val_t peo_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
|
||||||
@@ -877,6 +926,7 @@ el_val_t realize_vp_lang(el_val_t base_verb, el_val_t tense, el_val_t aspect, el
|
|||||||
el_val_t record(el_val_t outcome_json);
|
el_val_t record(el_val_t outcome_json);
|
||||||
el_val_t render_studio(void);
|
el_val_t render_studio(void);
|
||||||
el_val_t render_tree(el_val_t tree);
|
el_val_t render_tree(el_val_t tree);
|
||||||
|
el_val_t resolve_in_root(el_val_t path, el_val_t root);
|
||||||
el_val_t respond(el_val_t action_json);
|
el_val_t respond(el_val_t action_json);
|
||||||
el_val_t route_health(void);
|
el_val_t route_health(void);
|
||||||
el_val_t route_imprint_contextual(el_val_t body);
|
el_val_t route_imprint_contextual(el_val_t body);
|
||||||
@@ -936,12 +986,26 @@ el_val_t sa_str_ends(el_val_t s, el_val_t suf);
|
|||||||
el_val_t sa_vad_future(el_val_t slot);
|
el_val_t sa_vad_future(el_val_t slot);
|
||||||
el_val_t sa_vad_past(el_val_t slot);
|
el_val_t sa_vad_past(el_val_t slot);
|
||||||
el_val_t sa_vad_present(el_val_t slot);
|
el_val_t sa_vad_present(el_val_t slot);
|
||||||
|
el_val_t safety_abuse_phrases(void);
|
||||||
|
el_val_t safety_any_match(el_val_t text, el_val_t phrases_json);
|
||||||
|
el_val_t safety_augment_system(el_val_t system, el_val_t user_msg);
|
||||||
|
el_val_t safety_classify_hard_bell(el_val_t message);
|
||||||
|
el_val_t safety_contact_path(void);
|
||||||
|
el_val_t safety_count_match(el_val_t text, el_val_t phrases_json);
|
||||||
|
el_val_t safety_detect_bell_level(el_val_t message);
|
||||||
|
el_val_t safety_detect_positive_level(el_val_t message);
|
||||||
|
el_val_t safety_general_hard_phrases(void);
|
||||||
|
el_val_t safety_hard_directive(el_val_t hard_type);
|
||||||
el_val_t safety_log_bell(el_val_t level, el_val_t reason, el_val_t input_summary);
|
el_val_t safety_log_bell(el_val_t level, el_val_t reason, el_val_t input_summary);
|
||||||
|
el_val_t safety_normalize(el_val_t message);
|
||||||
el_val_t safety_score_crisis(el_val_t input);
|
el_val_t safety_score_crisis(el_val_t input);
|
||||||
el_val_t safety_score_danger(el_val_t input);
|
el_val_t safety_score_danger(el_val_t input);
|
||||||
el_val_t safety_score_distress_history(el_val_t history);
|
el_val_t safety_score_distress_history(el_val_t history);
|
||||||
el_val_t safety_score_harm(el_val_t input);
|
el_val_t safety_score_harm(el_val_t input);
|
||||||
el_val_t safety_screen(el_val_t input, el_val_t history);
|
el_val_t safety_screen(el_val_t input, el_val_t history);
|
||||||
|
el_val_t safety_self_harm_phrases(void);
|
||||||
|
el_val_t safety_soft_directive(void);
|
||||||
|
el_val_t safety_soft_phrases(void);
|
||||||
el_val_t safety_threat_score(el_val_t input, el_val_t history);
|
el_val_t safety_threat_score(el_val_t input, el_val_t history);
|
||||||
el_val_t safety_validate(el_val_t output, el_val_t action);
|
el_val_t safety_validate(el_val_t output, el_val_t action);
|
||||||
el_val_t scan_token(el_val_t s, el_val_t start);
|
el_val_t scan_token(el_val_t s, el_val_t start);
|
||||||
@@ -967,13 +1031,19 @@ el_val_t sem_to_spec(el_val_t frame);
|
|||||||
el_val_t sem_to_spec_full(el_val_t frame, el_val_t verb, el_val_t tense, el_val_t aspect);
|
el_val_t sem_to_spec_full(el_val_t frame, el_val_t verb, el_val_t tense, el_val_t aspect);
|
||||||
el_val_t session_auto_title(el_val_t session_id, el_val_t first_message);
|
el_val_t session_auto_title(el_val_t session_id, el_val_t first_message);
|
||||||
el_val_t session_create(el_val_t body);
|
el_val_t session_create(el_val_t body);
|
||||||
|
el_val_t session_create_cleanup(el_val_t session_id);
|
||||||
el_val_t session_delete(el_val_t session_id);
|
el_val_t session_delete(el_val_t session_id);
|
||||||
|
el_val_t session_exists(el_val_t session_id);
|
||||||
el_val_t session_get(el_val_t session_id);
|
el_val_t session_get(el_val_t session_id);
|
||||||
el_val_t session_hist_load(el_val_t session_id);
|
el_val_t session_hist_load(el_val_t session_id);
|
||||||
el_val_t session_hist_save(el_val_t session_id, el_val_t hist);
|
el_val_t session_hist_save(el_val_t session_id, el_val_t hist);
|
||||||
el_val_t session_list(void);
|
el_val_t session_list(void);
|
||||||
el_val_t session_make_content(el_val_t id, el_val_t title, el_val_t created_at, el_val_t updated_at, el_val_t folder);
|
el_val_t session_make_content(el_val_t id, el_val_t title, el_val_t created_at, el_val_t updated_at, el_val_t folder);
|
||||||
|
el_val_t session_preload_bullets(el_val_t nodes, el_val_t max_bullets, el_val_t snip_len);
|
||||||
el_val_t session_search(el_val_t query);
|
el_val_t session_search(el_val_t query);
|
||||||
|
el_val_t session_summary_autogenerate(el_val_t hist);
|
||||||
|
el_val_t session_summary_write(el_val_t summary_text);
|
||||||
|
el_val_t session_summary_write_dated(el_val_t summary_text, el_val_t label);
|
||||||
el_val_t session_title_from_message(el_val_t message);
|
el_val_t session_title_from_message(el_val_t message);
|
||||||
el_val_t session_update_meta_timestamp(el_val_t session_id);
|
el_val_t session_update_meta_timestamp(el_val_t session_id);
|
||||||
el_val_t session_update_patch(el_val_t session_id, el_val_t body);
|
el_val_t session_update_patch(el_val_t session_id, el_val_t body);
|
||||||
@@ -1018,6 +1088,7 @@ el_val_t str_last2(el_val_t s);
|
|||||||
el_val_t str_last3(el_val_t s);
|
el_val_t str_last3(el_val_t s);
|
||||||
el_val_t str_last_char(el_val_t s);
|
el_val_t str_last_char(el_val_t s);
|
||||||
el_val_t strengthen_chat_nodes(el_val_t activation_nodes);
|
el_val_t strengthen_chat_nodes(el_val_t activation_nodes);
|
||||||
|
el_val_t strip_citations(el_val_t s);
|
||||||
el_val_t strip_query(el_val_t path);
|
el_val_t strip_query(el_val_t path);
|
||||||
el_val_t studio_tools_json(void);
|
el_val_t studio_tools_json(void);
|
||||||
el_val_t sux_absolutive_suffix(el_val_t person, el_val_t number);
|
el_val_t sux_absolutive_suffix(el_val_t person, el_val_t number);
|
||||||
@@ -1078,6 +1149,7 @@ el_val_t threat_trajectory_check(el_val_t tool_name, el_val_t tool_input);
|
|||||||
el_val_t tier_canonical(void);
|
el_val_t tier_canonical(void);
|
||||||
el_val_t tier_episodic(void);
|
el_val_t tier_episodic(void);
|
||||||
el_val_t tier_working(void);
|
el_val_t tier_working(void);
|
||||||
|
el_val_t tool_auto_approved(el_val_t tool_name);
|
||||||
el_val_t txb_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
|
el_val_t txb_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
|
||||||
el_val_t txb_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
|
el_val_t txb_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
|
||||||
el_val_t txb_decline_fem(el_val_t noun, el_val_t gram_case, el_val_t number);
|
el_val_t txb_decline_fem(el_val_t noun, el_val_t gram_case, el_val_t number);
|
||||||
|
|||||||
-25003
File diff suppressed because it is too large
Load Diff
+34
-24028
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header — do not edit
|
||||||
extern fn sem_get(json: String, key: String) -> String
|
extern fn sem_get(json: String, key: String) -> String
|
||||||
extern fn generate_frame(frame: Any) -> String
|
extern fn generate_frame(frame: [String]) -> String
|
||||||
extern fn generate_frame_lang(frame: Any, lang_code: String) -> String
|
extern fn generate_frame_lang(frame: [String], lang_code: String) -> String
|
||||||
extern fn build_form_from_json(semantic_form_json: String, lang_code: String) -> Any
|
extern fn build_form_from_json(semantic_form_json: String, lang_code: String) -> [String]
|
||||||
extern fn generate(semantic_form_json: String) -> String
|
extern fn generate(semantic_form_json: String) -> String
|
||||||
extern fn generate_lang(semantic_form_json: String, lang_code: String) -> String
|
extern fn generate_lang(semantic_form_json: String, lang_code: String) -> String
|
||||||
|
|||||||
-5
@@ -656,8 +656,3 @@ el_val_t generate_tree(el_val_t rule_id_str, el_val_t slots) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int _argc, char** _argv) {
|
|
||||||
el_runtime_init_args(_argc, _argv);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
+28
-28
@@ -1,22 +1,22 @@
|
|||||||
// auto-generated by elc --emit-header - do not edit
|
// auto-generated by elc --emit-header — do not edit
|
||||||
extern fn slots_get(slots: Any, key: String) -> String
|
extern fn slots_get(slots: [String], key: String) -> String
|
||||||
extern fn slots_set(slots: Any, key: String, val: String) -> Any
|
extern fn slots_set(slots: [String], key: String, val: String) -> [String]
|
||||||
extern fn make_slots(k0: String, v0: String) -> Any
|
extern fn make_slots(k0: String, v0: String) -> [String]
|
||||||
extern fn make_slots2(k0: String, v0: String, k1: String, v1: String) -> Any
|
extern fn make_slots2(k0: String, v0: String, k1: String, v1: String) -> [String]
|
||||||
extern fn make_slots3(k0: String, v0: String, k1: String, v1: String, k2: String, v2: String) -> Any
|
extern fn make_slots3(k0: String, v0: String, k1: String, v1: String, k2: String, v2: String) -> [String]
|
||||||
extern fn make_slots4(k0: String, v0: String, k1: String, v1: String, k2: String, v2: String, k3: String, v3: String) -> Any
|
extern fn make_slots4(k0: String, v0: String, k1: String, v1: String, k2: String, v2: String, k3: String, v3: String) -> [String]
|
||||||
extern fn make_slots5(k0: String, v0: String, k1: String, v1: String, k2: String, v2: String, k3: String, v3: String, k4: String, v4: String) -> Any
|
extern fn make_slots5(k0: String, v0: String, k1: String, v1: String, k2: String, v2: String, k3: String, v3: String, k4: String, v4: String) -> [String]
|
||||||
extern fn rule_id(rule: Any) -> String
|
extern fn rule_id(rule: [String]) -> String
|
||||||
extern fn rule_lhs(rule: Any) -> String
|
extern fn rule_lhs(rule: [String]) -> String
|
||||||
extern fn rule_rhs_len(rule: Any) -> Int
|
extern fn rule_rhs_len(rule: [String]) -> Int
|
||||||
extern fn rule_rhs(rule: Any, idx: Int) -> String
|
extern fn rule_rhs(rule: [String], idx: Int) -> String
|
||||||
extern fn make_rule(id: String, lhs: String, r0: String) -> Any
|
extern fn make_rule(id: String, lhs: String, r0: String) -> [String]
|
||||||
extern fn make_rule2(id: String, lhs: String, r0: String, r1: String) -> Any
|
extern fn make_rule2(id: String, lhs: String, r0: String, r1: String) -> [String]
|
||||||
extern fn make_rule3(id: String, lhs: String, r0: String, r1: String, r2: String) -> Any
|
extern fn make_rule3(id: String, lhs: String, r0: String, r1: String, r2: String) -> [String]
|
||||||
extern fn make_rule4(id: String, lhs: String, r0: String, r1: String, r2: String, r3: String) -> Any
|
extern fn make_rule4(id: String, lhs: String, r0: String, r1: String, r2: String, r3: String) -> [String]
|
||||||
extern fn build_rules() -> Any
|
extern fn build_rules() -> [[String]]
|
||||||
extern fn get_rules() -> Any
|
extern fn get_rules() -> [[String]]
|
||||||
extern fn find_rule(rule_id_str: String) -> Any
|
extern fn find_rule(rule_id_str: String) -> [String]
|
||||||
extern fn make_leaf(label: String, word: String) -> String
|
extern fn make_leaf(label: String, word: String) -> String
|
||||||
extern fn make_node1(label: String, child0: String) -> String
|
extern fn make_node1(label: String, child0: String) -> String
|
||||||
extern fn make_node2(label: String, child0: String, child1: String) -> String
|
extern fn make_node2(label: String, child0: String, child1: String) -> String
|
||||||
@@ -24,15 +24,15 @@ extern fn make_node3(label: String, child0: String, child1: String, child2: Stri
|
|||||||
extern fn make_node4(label: String, child0: String, child1: String, child2: String, child3: String) -> String
|
extern fn make_node4(label: String, child0: String, child1: String, child2: String, child3: String) -> String
|
||||||
extern fn nlg_is_ws(c: String) -> Bool
|
extern fn nlg_is_ws(c: String) -> Bool
|
||||||
extern fn skip_ws(s: String, pos: Int) -> Int
|
extern fn skip_ws(s: String, pos: Int) -> Int
|
||||||
extern fn scan_token(s: String, start: Int) -> Any
|
extern fn scan_token(s: String, start: Int) -> [String]
|
||||||
extern fn render_tree(tree: String) -> String
|
extern fn render_tree(tree: String) -> String
|
||||||
extern fn gram_word_order(profile: Any) -> String
|
extern fn gram_word_order(profile: [String]) -> String
|
||||||
extern fn gram_order_constituents(subj: String, verb: String, obj: String, profile: Any) -> String
|
extern fn gram_order_constituents(subj: String, verb: String, obj: String, profile: [String]) -> String
|
||||||
extern fn gram_build_vp(verb: String, aux: String, profile: Any) -> String
|
extern fn gram_build_vp(verb: String, aux: String, profile: [String]) -> String
|
||||||
extern fn gram_question_strategy(profile: Any) -> String
|
extern fn gram_question_strategy(profile: [String]) -> String
|
||||||
extern fn is_pronoun(word: String) -> Bool
|
extern fn is_pronoun(word: String) -> Bool
|
||||||
extern fn build_np(referent: String, slots: Any) -> String
|
extern fn build_np(referent: String, slots: [String]) -> String
|
||||||
extern fn build_pp(loc: String) -> String
|
extern fn build_pp(loc: String) -> String
|
||||||
extern fn build_vp_body(slots: Any) -> String
|
extern fn build_vp_body(slots: [String]) -> String
|
||||||
extern fn build_vp_from_slots(slots: Any) -> String
|
extern fn build_vp_from_slots(slots: [String]) -> String
|
||||||
extern fn generate_tree(rule_id_str: String, slots: Any) -> String
|
extern fn generate_tree(rule_id_str: String, slots: [String]) -> String
|
||||||
|
|||||||
-5
@@ -70,8 +70,3 @@ el_val_t imprint_unload(void) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int _argc, char** _argv) {
|
|
||||||
el_runtime_init_args(_argc, _argv);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
-5
@@ -392,8 +392,3 @@ el_val_t lang_code(el_val_t profile) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int _argc, char** _argv) {
|
|
||||||
el_runtime_init_args(_argc, _argv);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
+58
-12
@@ -34,7 +34,18 @@ el_val_t tier_canonical(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
el_val_t mem_store(el_val_t content, el_val_t label, el_val_t tags) {
|
el_val_t mem_store(el_val_t content, el_val_t label, el_val_t tags) {
|
||||||
return engram_node_full(content, EL_STR("Memory"), label, el_from_float(el_from_float(0.5)), el_from_float(el_from_float(0.5)), el_from_float(el_from_float(0.8)), EL_STR("Working"), tags);
|
el_val_t id = engram_node_full(content, EL_STR("Memory"), label, el_from_float(0.5), el_from_float(0.5), el_from_float(0.8), EL_STR("Working"), tags);
|
||||||
|
if (str_eq(id, EL_STR(""))) {
|
||||||
|
println(el_str_concat(EL_STR("[memory] write rejected by engram (empty id): label="), label));
|
||||||
|
return EL_STR("");
|
||||||
|
}
|
||||||
|
el_val_t readback = engram_get_node_json(id);
|
||||||
|
if (str_eq(readback, EL_STR("")) || str_eq(readback, EL_STR("{}"))) {
|
||||||
|
println(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("[memory] WRITE VERIFY FAILED: label="), label), EL_STR(" id=")), id), EL_STR(" \xe2\x80\x94 node absent after write")));
|
||||||
|
return EL_STR("");
|
||||||
|
}
|
||||||
|
println(el_str_concat(el_str_concat(EL_STR("[memory] write verified: "), id), EL_STR(" ok")));
|
||||||
|
return id;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,15 +76,43 @@ el_val_t mem_forget(el_val_t node_id) {
|
|||||||
|
|
||||||
el_val_t mem_consolidate(void) {
|
el_val_t mem_consolidate(void) {
|
||||||
el_val_t scanned = engram_node_count();
|
el_val_t scanned = engram_node_count();
|
||||||
el_val_t dummy = engram_scan_nodes_json(100, 0);
|
|
||||||
el_val_t total_nodes = engram_node_count();
|
|
||||||
el_val_t total_edges = engram_edge_count();
|
el_val_t total_edges = engram_edge_count();
|
||||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"scanned\":"), int_to_str(scanned)), EL_STR(",\"total_nodes\":")), int_to_str(total_nodes)), EL_STR(",\"total_edges\":")), int_to_str(total_edges)), EL_STR("}"));
|
el_val_t strengthened = 0;
|
||||||
|
el_val_t wm_top = engram_wm_top_json(10);
|
||||||
|
el_val_t wm_len = json_array_len(wm_top);
|
||||||
|
el_val_t wi = 0;
|
||||||
|
while (wi < wm_len) {
|
||||||
|
el_val_t wm_node = json_array_get(wm_top, wi);
|
||||||
|
el_val_t wm_id = json_get(wm_node, EL_STR("id"));
|
||||||
|
if (!str_eq(wm_id, EL_STR(""))) {
|
||||||
|
engram_strengthen(wm_id);
|
||||||
|
strengthened = (strengthened + 1);
|
||||||
|
}
|
||||||
|
wi = (wi + 1);
|
||||||
|
}
|
||||||
|
el_val_t scan_result = engram_scan_nodes_json(50, 0);
|
||||||
|
el_val_t scan_len = json_array_len(scan_result);
|
||||||
|
el_val_t si = 0;
|
||||||
|
while (si < scan_len) {
|
||||||
|
el_val_t s_node = json_array_get(scan_result, si);
|
||||||
|
el_val_t s_tier = json_get(s_node, EL_STR("tier"));
|
||||||
|
el_val_t s_id = json_get(s_node, EL_STR("id"));
|
||||||
|
if (str_eq(s_tier, EL_STR("Canonical")) && !str_eq(s_id, EL_STR(""))) {
|
||||||
|
engram_strengthen(s_id);
|
||||||
|
strengthened = (strengthened + 1);
|
||||||
|
}
|
||||||
|
si = (si + 1);
|
||||||
|
}
|
||||||
|
el_val_t total_nodes = engram_node_count();
|
||||||
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"scanned\":"), int_to_str(scanned)), EL_STR(",\"total_nodes\":")), int_to_str(total_nodes)), EL_STR(",\"total_edges\":")), int_to_str(total_edges)), EL_STR(",\"strengthened\":")), int_to_str(strengthened)), EL_STR("}"));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
el_val_t mem_save(el_val_t path) {
|
el_val_t mem_save(el_val_t path) {
|
||||||
engram_save(path);
|
el_val_t save_result = engram_save(path);
|
||||||
|
if (str_eq(save_result, EL_STR(""))) {
|
||||||
|
println(el_str_concat(el_str_concat(EL_STR("[memory] mem_save: engram_save failed for "), path), EL_STR(" \xe2\x80\x94 snapshot may be incomplete")));
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +145,15 @@ el_val_t mem_boot_count_inc(void) {
|
|||||||
el_val_t next = (current + 1);
|
el_val_t next = (current + 1);
|
||||||
el_val_t content = el_str_concat(EL_STR("soul:boot_count:"), int_to_str(next));
|
el_val_t content = el_str_concat(EL_STR("soul:boot_count:"), int_to_str(next));
|
||||||
el_val_t tags = EL_STR("[\"soul-meta\",\"boot-counter\"]");
|
el_val_t tags = EL_STR("[\"soul-meta\",\"boot-counter\"]");
|
||||||
el_val_t discard = engram_node_full(content, EL_STR("Memory"), EL_STR("soul:boot_count"), el_from_float(el_from_float(0.9)), el_from_float(el_from_float(0.9)), el_from_float(el_from_float(1.0)), EL_STR("Canonical"), tags);
|
el_val_t boot_node_id = engram_node_full(content, EL_STR("Memory"), EL_STR("soul:boot_count"), el_from_float(0.9), el_from_float(0.9), el_from_float(1.0), EL_STR("Canonical"), tags);
|
||||||
|
if (str_eq(boot_node_id, EL_STR(""))) {
|
||||||
|
println(el_str_concat(el_str_concat(EL_STR("[memory] mem_boot_count_inc: write rejected (empty id) \xe2\x80\x94 boot counter node lost (count="), int_to_str(next)), EL_STR(")")));
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
el_val_t boot_readback = engram_get_node_json(boot_node_id);
|
||||||
|
if (str_eq(boot_readback, EL_STR("")) || str_eq(boot_readback, EL_STR("{}"))) {
|
||||||
|
println(el_str_concat(el_str_concat(el_str_concat(EL_STR("[memory] mem_boot_count_inc: WRITE VERIFY FAILED id="), boot_node_id), EL_STR(" count=")), int_to_str(next)));
|
||||||
|
}
|
||||||
return next;
|
return next;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -118,12 +165,11 @@ el_val_t mem_emit_state_event(el_val_t trigger, el_val_t kind, el_val_t content)
|
|||||||
el_val_t safe_content = str_replace(content, EL_STR("\""), EL_STR("'"));
|
el_val_t safe_content = str_replace(content, EL_STR("\""), EL_STR("'"));
|
||||||
el_val_t payload = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"trigger\":\""), safe_trigger), EL_STR("\"")), EL_STR(",\"kind\":\"")), kind), EL_STR("\"")), EL_STR(",\"content\":\"")), safe_content), EL_STR("\"")), EL_STR(",\"boot\":")), int_to_str(boot)), EL_STR(",\"ts\":")), int_to_str(ts)), EL_STR("}"));
|
el_val_t payload = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"trigger\":\""), safe_trigger), EL_STR("\"")), EL_STR(",\"kind\":\"")), kind), EL_STR("\"")), EL_STR(",\"content\":\"")), safe_content), EL_STR("\"")), EL_STR(",\"boot\":")), int_to_str(boot)), EL_STR(",\"ts\":")), int_to_str(ts)), EL_STR("}"));
|
||||||
el_val_t tags = EL_STR("[\"internal-state\",\"pre-reasoning\",\"InternalStateEvent\"]");
|
el_val_t tags = EL_STR("[\"internal-state\",\"pre-reasoning\",\"InternalStateEvent\"]");
|
||||||
return engram_node_full(payload, EL_STR("InternalStateEvent"), el_str_concat(EL_STR("state-event:"), kind), el_from_float(el_from_float(0.85)), el_from_float(el_from_float(0.8)), el_from_float(el_from_float(0.9)), EL_STR("Episodic"), tags);
|
el_val_t event_id = engram_node_full(payload, EL_STR("InternalStateEvent"), el_str_concat(EL_STR("state-event:"), kind), el_from_float(0.85), el_from_float(0.8), el_from_float(0.9), EL_STR("Episodic"), tags);
|
||||||
return 0;
|
if (str_eq(event_id, EL_STR(""))) {
|
||||||
}
|
println(el_str_concat(EL_STR("[memory] mem_emit_state_event: write rejected (empty id): kind="), kind));
|
||||||
|
}
|
||||||
int main(int _argc, char** _argv) {
|
return event_id;
|
||||||
el_runtime_init_args(_argc, _argv);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+204
-161
@@ -26,9 +26,14 @@ el_val_t api_ok(el_val_t extra);
|
|||||||
el_val_t api_err(el_val_t msg);
|
el_val_t api_err(el_val_t msg);
|
||||||
el_val_t api_nonempty(el_val_t s);
|
el_val_t api_nonempty(el_val_t s);
|
||||||
el_val_t api_or_empty(el_val_t s);
|
el_val_t api_or_empty(el_val_t s);
|
||||||
|
el_val_t api_persisted(el_val_t id);
|
||||||
|
el_val_t api_not_persisted(el_val_t id);
|
||||||
el_val_t handle_api_begin_session(el_val_t body);
|
el_val_t handle_api_begin_session(el_val_t body);
|
||||||
el_val_t handle_api_compile_ctx(el_val_t body);
|
el_val_t handle_api_compile_ctx(el_val_t body);
|
||||||
el_val_t handle_api_remember(el_val_t body);
|
el_val_t handle_api_remember(el_val_t body);
|
||||||
|
el_val_t handle_api_node_create(el_val_t body);
|
||||||
|
el_val_t handle_api_node_delete(el_val_t body);
|
||||||
|
el_val_t handle_api_node_update(el_val_t body);
|
||||||
el_val_t handle_api_recall(el_val_t method, el_val_t path, el_val_t body);
|
el_val_t handle_api_recall(el_val_t method, el_val_t path, el_val_t body);
|
||||||
el_val_t handle_api_search_knowledge(el_val_t method, el_val_t path, el_val_t body);
|
el_val_t handle_api_search_knowledge(el_val_t method, el_val_t path, el_val_t body);
|
||||||
el_val_t handle_api_browse_knowledge(el_val_t path, el_val_t body);
|
el_val_t handle_api_browse_knowledge(el_val_t path, el_val_t body);
|
||||||
@@ -45,114 +50,12 @@ el_val_t handle_api_inspect_graph(el_val_t method, el_val_t path, el_val_t body)
|
|||||||
el_val_t handle_api_link_entities(el_val_t body);
|
el_val_t handle_api_link_entities(el_val_t body);
|
||||||
el_val_t handle_api_forget(el_val_t body);
|
el_val_t handle_api_forget(el_val_t body);
|
||||||
el_val_t handle_api_evolve_memory(el_val_t body);
|
el_val_t handle_api_evolve_memory(el_val_t body);
|
||||||
|
el_val_t handle_api_memory_delete(el_val_t body);
|
||||||
|
el_val_t handle_api_memory_update(el_val_t body);
|
||||||
el_val_t handle_api_cultivate(el_val_t body);
|
el_val_t handle_api_cultivate(el_val_t body);
|
||||||
el_val_t handle_api_list_typed(el_val_t node_type, el_val_t path, el_val_t body);
|
el_val_t handle_api_list_typed(el_val_t node_type, el_val_t path, el_val_t body);
|
||||||
el_val_t handle_api_consolidate(el_val_t body);
|
el_val_t handle_api_consolidate(el_val_t body);
|
||||||
|
|
||||||
el_val_t tier_working(void) {
|
|
||||||
return EL_STR("Working");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t tier_episodic(void) {
|
|
||||||
return EL_STR("Episodic");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t tier_canonical(void) {
|
|
||||||
return EL_STR("Canonical");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_store(el_val_t content, el_val_t label, el_val_t tags) {
|
|
||||||
return engram_node_full(content, EL_STR("Memory"), label, el_from_float(el_from_float(0.5)), el_from_float(el_from_float(0.5)), el_from_float(el_from_float(0.8)), EL_STR("Working"), tags);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_remember(el_val_t content, el_val_t tags) {
|
|
||||||
return mem_store(content, EL_STR("soul-memory"), tags);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_recall(el_val_t query, el_val_t depth) {
|
|
||||||
return engram_activate_json(query, depth);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_search(el_val_t query, el_val_t limit) {
|
|
||||||
return engram_search_json(query, limit);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_strengthen(el_val_t node_id) {
|
|
||||||
engram_strengthen(node_id);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_forget(el_val_t node_id) {
|
|
||||||
engram_forget(node_id);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_consolidate(void) {
|
|
||||||
el_val_t scanned = engram_node_count();
|
|
||||||
el_val_t dummy = engram_scan_nodes_json(100, 0);
|
|
||||||
el_val_t total_nodes = engram_node_count();
|
|
||||||
el_val_t total_edges = engram_edge_count();
|
|
||||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"scanned\":"), int_to_str(scanned)), EL_STR(",\"total_nodes\":")), int_to_str(total_nodes)), EL_STR(",\"total_edges\":")), int_to_str(total_edges)), EL_STR("}"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_save(el_val_t path) {
|
|
||||||
engram_save(path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_load(el_val_t path) {
|
|
||||||
engram_load(path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_boot_count_get(void) {
|
|
||||||
el_val_t results = engram_search_json(EL_STR("soul:boot_count"), 3);
|
|
||||||
if (str_eq(results, EL_STR(""))) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (str_eq(results, EL_STR("[]"))) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
el_val_t node = json_array_get(results, 0);
|
|
||||||
el_val_t content = json_get(node, EL_STR("content"));
|
|
||||||
el_val_t prefix = EL_STR("soul:boot_count:");
|
|
||||||
if (!str_starts_with(content, prefix)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
el_val_t num_str = str_slice(content, str_len(prefix), str_len(content));
|
|
||||||
return str_to_int(num_str);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_boot_count_inc(void) {
|
|
||||||
el_val_t current = mem_boot_count_get();
|
|
||||||
el_val_t next = (current + 1);
|
|
||||||
el_val_t content = el_str_concat(EL_STR("soul:boot_count:"), int_to_str(next));
|
|
||||||
el_val_t tags = EL_STR("[\"soul-meta\",\"boot-counter\"]");
|
|
||||||
el_val_t discard = engram_node_full(content, EL_STR("Memory"), EL_STR("soul:boot_count"), el_from_float(el_from_float(0.9)), el_from_float(el_from_float(0.9)), el_from_float(el_from_float(1.0)), EL_STR("Canonical"), tags);
|
|
||||||
return next;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_emit_state_event(el_val_t trigger, el_val_t kind, el_val_t content) {
|
|
||||||
el_val_t boot = mem_boot_count_get();
|
|
||||||
el_val_t ts = time_now();
|
|
||||||
el_val_t safe_trigger = str_replace(trigger, EL_STR("\""), EL_STR("'"));
|
|
||||||
el_val_t safe_content = str_replace(content, EL_STR("\""), EL_STR("'"));
|
|
||||||
el_val_t payload = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"trigger\":\""), safe_trigger), EL_STR("\"")), EL_STR(",\"kind\":\"")), kind), EL_STR("\"")), EL_STR(",\"content\":\"")), safe_content), EL_STR("\"")), EL_STR(",\"boot\":")), int_to_str(boot)), EL_STR(",\"ts\":")), int_to_str(ts)), EL_STR("}"));
|
|
||||||
el_val_t tags = EL_STR("[\"internal-state\",\"pre-reasoning\",\"InternalStateEvent\"]");
|
|
||||||
return engram_node_full(payload, EL_STR("InternalStateEvent"), el_str_concat(EL_STR("state-event:"), kind), el_from_float(el_from_float(0.85)), el_from_float(el_from_float(0.8)), el_from_float(el_from_float(0.9)), EL_STR("Episodic"), tags);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t is_protected_node(el_val_t id) {
|
el_val_t is_protected_node(el_val_t id) {
|
||||||
if (str_eq(id, EL_STR("kn-efeb4a5b-5aff-4759-8a97-7233099be6ee"))) {
|
if (str_eq(id, EL_STR("kn-efeb4a5b-5aff-4759-8a97-7233099be6ee"))) {
|
||||||
return 1;
|
return 1;
|
||||||
@@ -272,6 +175,20 @@ el_val_t api_or_empty(el_val_t s) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
el_val_t api_persisted(el_val_t id) {
|
||||||
|
if (str_eq(id, EL_STR(""))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
el_val_t node = engram_get_node_json(id);
|
||||||
|
return ((!str_eq(node, EL_STR("")) && !str_eq(node, EL_STR("null"))) && !str_eq(node, EL_STR("{}")));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
el_val_t api_not_persisted(el_val_t id) {
|
||||||
|
return el_str_concat(el_str_concat(EL_STR("{\"ok\":false,\"error\":\"write_not_persisted\",\"id\":\""), id), EL_STR("\"}"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
el_val_t handle_api_begin_session(el_val_t body) {
|
el_val_t handle_api_begin_session(el_val_t body) {
|
||||||
el_val_t stats = engram_stats_json();
|
el_val_t stats = engram_stats_json();
|
||||||
el_val_t activated = engram_activate_json(EL_STR("session start recent memory important"), 2);
|
el_val_t activated = engram_activate_json(EL_STR("session start recent memory important"), 2);
|
||||||
@@ -302,18 +219,88 @@ el_val_t handle_api_remember(el_val_t body) {
|
|||||||
el_val_t sal = ({ el_val_t _if_result_4 = 0; if (str_eq(sal_str, EL_STR("0.95"))) { _if_result_4 = (el_from_float(0.95)); } else { _if_result_4 = (({ el_val_t _if_result_5 = 0; if (str_eq(sal_str, EL_STR("0.75"))) { _if_result_5 = (el_from_float(0.75)); } else { _if_result_5 = (({ el_val_t _if_result_6 = 0; if (str_eq(sal_str, EL_STR("0.25"))) { _if_result_6 = (el_from_float(0.25)); } else { _if_result_6 = (el_from_float(0.5)); } _if_result_6; })); } _if_result_5; })); } _if_result_4; });
|
el_val_t sal = ({ el_val_t _if_result_4 = 0; if (str_eq(sal_str, EL_STR("0.95"))) { _if_result_4 = (el_from_float(0.95)); } else { _if_result_4 = (({ el_val_t _if_result_5 = 0; if (str_eq(sal_str, EL_STR("0.75"))) { _if_result_5 = (el_from_float(0.75)); } else { _if_result_5 = (({ el_val_t _if_result_6 = 0; if (str_eq(sal_str, EL_STR("0.25"))) { _if_result_6 = (el_from_float(0.25)); } else { _if_result_6 = (el_from_float(0.5)); } _if_result_6; })); } _if_result_5; })); } _if_result_4; });
|
||||||
el_val_t base_tags = ({ el_val_t _if_result_7 = 0; if (str_eq(tags_raw, EL_STR(""))) { _if_result_7 = (EL_STR("[\"Memory\"]")); } else { _if_result_7 = (tags_raw); } _if_result_7; });
|
el_val_t base_tags = ({ el_val_t _if_result_7 = 0; if (str_eq(tags_raw, EL_STR(""))) { _if_result_7 = (EL_STR("[\"Memory\"]")); } else { _if_result_7 = (tags_raw); } _if_result_7; });
|
||||||
el_val_t final_tags = ({ el_val_t _if_result_8 = 0; if (str_eq(project, EL_STR(""))) { _if_result_8 = (base_tags); } else { el_val_t inner = str_slice(base_tags, 1, (str_len(base_tags) - 1)); _if_result_8 = (el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("["), inner), EL_STR(",\"project:")), project), EL_STR("\"]"))); } _if_result_8; });
|
el_val_t final_tags = ({ el_val_t _if_result_8 = 0; if (str_eq(project, EL_STR(""))) { _if_result_8 = (base_tags); } else { el_val_t inner = str_slice(base_tags, 1, (str_len(base_tags) - 1)); _if_result_8 = (el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("["), inner), EL_STR(",\"project:")), project), EL_STR("\"]"))); } _if_result_8; });
|
||||||
el_val_t id = engram_node_full(content, EL_STR("Memory"), EL_STR("memory:remembered"), el_from_float(sal), el_from_float(sal), el_from_float(el_from_float(0.9)), EL_STR("Episodic"), final_tags);
|
el_val_t id = engram_node_full(content, EL_STR("Memory"), EL_STR("memory:remembered"), el_from_float(sal), el_from_float(sal), el_from_float(0.9), EL_STR("Episodic"), final_tags);
|
||||||
|
if (!api_persisted(id)) {
|
||||||
|
return api_not_persisted(id);
|
||||||
|
}
|
||||||
return el_str_concat(el_str_concat(EL_STR("{\"id\":\""), id), EL_STR("\",\"ok\":true}"));
|
return el_str_concat(el_str_concat(EL_STR("{\"id\":\""), id), EL_STR("\",\"ok\":true}"));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
el_val_t handle_api_node_create(el_val_t body) {
|
||||||
|
el_val_t content = json_get(body, EL_STR("content"));
|
||||||
|
if (str_eq(content, EL_STR(""))) {
|
||||||
|
return api_err(EL_STR("content is required"));
|
||||||
|
}
|
||||||
|
el_val_t nt_raw = json_get(body, EL_STR("node_type"));
|
||||||
|
el_val_t node_type = ({ el_val_t _if_result_9 = 0; if (str_eq(nt_raw, EL_STR(""))) { _if_result_9 = (EL_STR("Memory")); } else { _if_result_9 = (nt_raw); } _if_result_9; });
|
||||||
|
el_val_t label_raw = json_get(body, EL_STR("label"));
|
||||||
|
el_val_t label = ({ el_val_t _if_result_10 = 0; if (str_eq(label_raw, EL_STR(""))) { _if_result_10 = (EL_STR("node:created")); } else { _if_result_10 = (label_raw); } _if_result_10; });
|
||||||
|
el_val_t tier_raw = json_get(body, EL_STR("tier"));
|
||||||
|
el_val_t tier = ({ el_val_t _if_result_11 = 0; if (str_eq(tier_raw, EL_STR(""))) { _if_result_11 = (EL_STR("Episodic")); } else { _if_result_11 = (tier_raw); } _if_result_11; });
|
||||||
|
el_val_t tags_raw = json_get(body, EL_STR("tags"));
|
||||||
|
el_val_t tags = ({ el_val_t _if_result_12 = 0; if (str_eq(tags_raw, EL_STR(""))) { _if_result_12 = (el_str_concat(el_str_concat(EL_STR("[\""), node_type), EL_STR("\"]"))); } else { _if_result_12 = (tags_raw); } _if_result_12; });
|
||||||
|
el_val_t importance = json_get(body, EL_STR("importance"));
|
||||||
|
el_val_t sal = ({ el_val_t _if_result_13 = 0; if (str_eq(importance, EL_STR("critical"))) { _if_result_13 = (el_from_float(0.95)); } else { _if_result_13 = (({ el_val_t _if_result_14 = 0; if (str_eq(importance, EL_STR("high"))) { _if_result_14 = (el_from_float(0.75)); } else { _if_result_14 = (({ el_val_t _if_result_15 = 0; if (str_eq(importance, EL_STR("low"))) { _if_result_15 = (el_from_float(0.25)); } else { _if_result_15 = (el_from_float(0.5)); } _if_result_15; })); } _if_result_14; })); } _if_result_13; });
|
||||||
|
el_val_t id = engram_node_full(content, node_type, label, el_from_float(sal), el_from_float(sal), el_from_float(0.9), tier, tags);
|
||||||
|
if (!api_persisted(id)) {
|
||||||
|
return api_not_persisted(id);
|
||||||
|
}
|
||||||
|
return el_str_concat(el_str_concat(EL_STR("{\"id\":\""), id), EL_STR("\",\"ok\":true}"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
el_val_t handle_api_node_delete(el_val_t body) {
|
||||||
|
el_val_t id = json_get(body, EL_STR("id"));
|
||||||
|
if (str_eq(id, EL_STR(""))) {
|
||||||
|
return api_err(EL_STR("id is required"));
|
||||||
|
}
|
||||||
|
engram_forget(id);
|
||||||
|
return el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"id\":\""), id), EL_STR("\"}"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
el_val_t handle_api_node_update(el_val_t body) {
|
||||||
|
el_val_t id = json_get(body, EL_STR("id"));
|
||||||
|
if (str_eq(id, EL_STR(""))) {
|
||||||
|
return api_err(EL_STR("id is required"));
|
||||||
|
}
|
||||||
|
if (!api_persisted(id)) {
|
||||||
|
return el_str_concat(el_str_concat(EL_STR("{\"ok\":false,\"error\":\"not_found\",\"id\":\""), id), EL_STR("\"}"));
|
||||||
|
}
|
||||||
|
el_val_t old = engram_get_node_json(id);
|
||||||
|
el_val_t body_content = json_get(body, EL_STR("content"));
|
||||||
|
el_val_t content = ({ el_val_t _if_result_16 = 0; if (str_eq(body_content, EL_STR(""))) { _if_result_16 = (json_get(old, EL_STR("content"))); } else { _if_result_16 = (body_content); } _if_result_16; });
|
||||||
|
el_val_t body_nt = json_get(body, EL_STR("node_type"));
|
||||||
|
el_val_t old_nt = json_get(old, EL_STR("node_type"));
|
||||||
|
el_val_t node_type = ({ el_val_t _if_result_17 = 0; if (!str_eq(body_nt, EL_STR(""))) { _if_result_17 = (body_nt); } else { _if_result_17 = (({ el_val_t _if_result_18 = 0; if (!str_eq(old_nt, EL_STR(""))) { _if_result_18 = (old_nt); } else { _if_result_18 = (EL_STR("Memory")); } _if_result_18; })); } _if_result_17; });
|
||||||
|
el_val_t body_label = json_get(body, EL_STR("label"));
|
||||||
|
el_val_t old_label = json_get(old, EL_STR("label"));
|
||||||
|
el_val_t label = ({ el_val_t _if_result_19 = 0; if (!str_eq(body_label, EL_STR(""))) { _if_result_19 = (body_label); } else { _if_result_19 = (({ el_val_t _if_result_20 = 0; if (!str_eq(old_label, EL_STR(""))) { _if_result_20 = (old_label); } else { _if_result_20 = (EL_STR("node:updated")); } _if_result_20; })); } _if_result_19; });
|
||||||
|
el_val_t body_tier = json_get(body, EL_STR("tier"));
|
||||||
|
el_val_t old_tier = json_get(old, EL_STR("tier"));
|
||||||
|
el_val_t tier = ({ el_val_t _if_result_21 = 0; if (!str_eq(body_tier, EL_STR(""))) { _if_result_21 = (body_tier); } else { _if_result_21 = (({ el_val_t _if_result_22 = 0; if (!str_eq(old_tier, EL_STR(""))) { _if_result_22 = (old_tier); } else { _if_result_22 = (EL_STR("Episodic")); } _if_result_22; })); } _if_result_21; });
|
||||||
|
el_val_t body_tags = json_get(body, EL_STR("tags"));
|
||||||
|
el_val_t tags = ({ el_val_t _if_result_23 = 0; if (str_eq(body_tags, EL_STR(""))) { _if_result_23 = (el_str_concat(el_str_concat(EL_STR("[\""), node_type), EL_STR("\"]"))); } else { _if_result_23 = (body_tags); } _if_result_23; });
|
||||||
|
el_val_t new_id = engram_node_full(content, node_type, label, el_from_float(0.5), el_from_float(0.5), el_from_float(0.8), tier, tags);
|
||||||
|
if (!api_persisted(new_id)) {
|
||||||
|
return api_not_persisted(new_id);
|
||||||
|
}
|
||||||
|
engram_forget(id);
|
||||||
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"id\":\""), new_id), EL_STR("\",\"replaced\":\"")), id), EL_STR("\",\"ok\":true}"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
el_val_t handle_api_recall(el_val_t method, el_val_t path, el_val_t body) {
|
el_val_t handle_api_recall(el_val_t method, el_val_t path, el_val_t body) {
|
||||||
el_val_t q = ({ el_val_t _if_result_9 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_9 = (api_query_param(path, EL_STR("query"))); } else { _if_result_9 = (json_get(body, EL_STR("query"))); } _if_result_9; });
|
el_val_t url_q = ({ el_val_t _if_result_24 = 0; if (str_eq(api_query_param(path, EL_STR("query")), EL_STR(""))) { _if_result_24 = (api_query_param(path, EL_STR("q"))); } else { _if_result_24 = (api_query_param(path, EL_STR("query"))); } _if_result_24; });
|
||||||
|
el_val_t body_query = json_get(body, EL_STR("query"));
|
||||||
|
el_val_t body_q = json_get(body, EL_STR("q"));
|
||||||
|
el_val_t q = ({ el_val_t _if_result_25 = 0; if (!str_eq(url_q, EL_STR(""))) { _if_result_25 = (url_q); } else { _if_result_25 = (({ el_val_t _if_result_26 = 0; if (!str_eq(body_query, EL_STR(""))) { _if_result_26 = (body_query); } else { _if_result_26 = (body_q); } _if_result_26; })); } _if_result_25; });
|
||||||
el_val_t chain = json_get(body, EL_STR("chain_name"));
|
el_val_t chain = json_get(body, EL_STR("chain_name"));
|
||||||
el_val_t limit = api_query_int(path, EL_STR("limit"), 0);
|
el_val_t limit = api_query_int(path, EL_STR("limit"), 0);
|
||||||
limit = ({ el_val_t _if_result_10 = 0; if ((limit == 0)) { _if_result_10 = (json_get_int(body, EL_STR("limit"))); } else { _if_result_10 = (limit); } _if_result_10; });
|
limit = ({ el_val_t _if_result_27 = 0; if ((limit == 0)) { _if_result_27 = (json_get_int(body, EL_STR("limit"))); } else { _if_result_27 = (limit); } _if_result_27; });
|
||||||
limit = ({ el_val_t _if_result_11 = 0; if ((limit == 0)) { _if_result_11 = (10); } else { _if_result_11 = (limit); } _if_result_11; });
|
limit = ({ el_val_t _if_result_28 = 0; if ((limit == 0)) { _if_result_28 = (10); } else { _if_result_28 = (limit); } _if_result_28; });
|
||||||
el_val_t eff_q = ({ el_val_t _if_result_12 = 0; if (str_eq(q, EL_STR(""))) { _if_result_12 = (chain); } else { _if_result_12 = (q); } _if_result_12; });
|
el_val_t eff_q = ({ el_val_t _if_result_29 = 0; if (str_eq(q, EL_STR(""))) { _if_result_29 = (chain); } else { _if_result_29 = (q); } _if_result_29; });
|
||||||
if (str_eq(eff_q, EL_STR(""))) {
|
if (str_eq(eff_q, EL_STR(""))) {
|
||||||
return api_or_empty(engram_scan_nodes_json(limit, 0));
|
return api_or_empty(engram_scan_nodes_json(limit, 0));
|
||||||
}
|
}
|
||||||
@@ -323,10 +310,13 @@ el_val_t handle_api_recall(el_val_t method, el_val_t path, el_val_t body) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
el_val_t handle_api_search_knowledge(el_val_t method, el_val_t path, el_val_t body) {
|
el_val_t handle_api_search_knowledge(el_val_t method, el_val_t path, el_val_t body) {
|
||||||
el_val_t q = ({ el_val_t _if_result_13 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_13 = (api_query_param(path, EL_STR("q"))); } else { _if_result_13 = (json_get(body, EL_STR("query"))); } _if_result_13; });
|
el_val_t url_q = api_query_param(path, EL_STR("q"));
|
||||||
|
el_val_t body_query = json_get(body, EL_STR("query"));
|
||||||
|
el_val_t body_q = json_get(body, EL_STR("q"));
|
||||||
|
el_val_t q = ({ el_val_t _if_result_30 = 0; if (!str_eq(url_q, EL_STR(""))) { _if_result_30 = (url_q); } else { _if_result_30 = (({ el_val_t _if_result_31 = 0; if (!str_eq(body_query, EL_STR(""))) { _if_result_31 = (body_query); } else { _if_result_31 = (body_q); } _if_result_31; })); } _if_result_30; });
|
||||||
el_val_t limit = api_query_int(path, EL_STR("limit"), 0);
|
el_val_t limit = api_query_int(path, EL_STR("limit"), 0);
|
||||||
limit = ({ el_val_t _if_result_14 = 0; if ((limit == 0)) { _if_result_14 = (json_get_int(body, EL_STR("limit"))); } else { _if_result_14 = (limit); } _if_result_14; });
|
limit = ({ el_val_t _if_result_32 = 0; if ((limit == 0)) { _if_result_32 = (json_get_int(body, EL_STR("limit"))); } else { _if_result_32 = (limit); } _if_result_32; });
|
||||||
limit = ({ el_val_t _if_result_15 = 0; if ((limit == 0)) { _if_result_15 = (10); } else { _if_result_15 = (limit); } _if_result_15; });
|
limit = ({ el_val_t _if_result_33 = 0; if ((limit == 0)) { _if_result_33 = (10); } else { _if_result_33 = (limit); } _if_result_33; });
|
||||||
if (str_eq(q, EL_STR(""))) {
|
if (str_eq(q, EL_STR(""))) {
|
||||||
return api_err(EL_STR("query is required"));
|
return api_err(EL_STR("query is required"));
|
||||||
}
|
}
|
||||||
@@ -354,9 +344,12 @@ el_val_t handle_api_capture_knowledge(el_val_t body) {
|
|||||||
if (str_eq(content, EL_STR(""))) {
|
if (str_eq(content, EL_STR(""))) {
|
||||||
return api_err(EL_STR("content is required"));
|
return api_err(EL_STR("content is required"));
|
||||||
}
|
}
|
||||||
el_val_t full = ({ el_val_t _if_result_16 = 0; if (str_eq(title, EL_STR(""))) { _if_result_16 = (content); } else { _if_result_16 = (el_str_concat(el_str_concat(title, EL_STR(": ")), content)); } _if_result_16; });
|
el_val_t full = ({ el_val_t _if_result_34 = 0; if (str_eq(title, EL_STR(""))) { _if_result_34 = (content); } else { _if_result_34 = (el_str_concat(el_str_concat(title, EL_STR(": ")), content)); } _if_result_34; });
|
||||||
el_val_t tags = EL_STR("[\"Knowledge\",\"captured\"]");
|
el_val_t tags = EL_STR("[\"Knowledge\",\"captured\"]");
|
||||||
el_val_t id = engram_node_full(full, EL_STR("Knowledge"), EL_STR("knowledge:captured"), el_from_float(el_from_float(0.85)), el_from_float(el_from_float(0.8)), el_from_float(el_from_float(0.9)), EL_STR("Episodic"), tags);
|
el_val_t id = engram_node_full(full, EL_STR("Knowledge"), EL_STR("knowledge:captured"), el_from_float(0.85), el_from_float(0.8), el_from_float(0.9), EL_STR("Episodic"), tags);
|
||||||
|
if (!api_persisted(id)) {
|
||||||
|
return api_not_persisted(id);
|
||||||
|
}
|
||||||
return el_str_concat(el_str_concat(EL_STR("{\"id\":\""), id), EL_STR("\",\"ok\":true}"));
|
return el_str_concat(el_str_concat(EL_STR("{\"id\":\""), id), EL_STR("\",\"ok\":true}"));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -371,9 +364,12 @@ el_val_t handle_api_evolve_knowledge(el_val_t body) {
|
|||||||
return api_err_protected(prior_id);
|
return api_err_protected(prior_id);
|
||||||
}
|
}
|
||||||
el_val_t tags = EL_STR("[\"Knowledge\",\"evolved\"]");
|
el_val_t tags = EL_STR("[\"Knowledge\",\"evolved\"]");
|
||||||
el_val_t new_id = engram_node_full(content, EL_STR("Knowledge"), EL_STR("knowledge:evolved"), el_from_float(el_from_float(0.75)), el_from_float(el_from_float(0.75)), el_from_float(el_from_float(0.9)), EL_STR("Episodic"), tags);
|
el_val_t new_id = engram_node_full(content, EL_STR("Knowledge"), EL_STR("knowledge:evolved"), el_from_float(0.75), el_from_float(0.75), el_from_float(0.9), EL_STR("Episodic"), tags);
|
||||||
if (!str_eq(prior_id, EL_STR("")) && !str_eq(new_id, EL_STR(""))) {
|
if (!api_persisted(new_id)) {
|
||||||
engram_connect(new_id, prior_id, el_from_float(el_from_float(0.9)), EL_STR("supersedes"));
|
return api_not_persisted(new_id);
|
||||||
|
}
|
||||||
|
if (!str_eq(prior_id, EL_STR(""))) {
|
||||||
|
engram_connect(new_id, prior_id, el_from_float(0.9), EL_STR("supersedes"));
|
||||||
}
|
}
|
||||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"id\":\""), new_id), EL_STR("\",\"supersedes\":\"")), prior_id), EL_STR("\",\"ok\":true}"));
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"id\":\""), new_id), EL_STR("\",\"supersedes\":\"")), prior_id), EL_STR("\",\"ok\":true}"));
|
||||||
return 0;
|
return 0;
|
||||||
@@ -389,18 +385,18 @@ el_val_t handle_api_promote_knowledge(el_val_t body) {
|
|||||||
return api_err(EL_STR("id (prior node) is required"));
|
return api_err(EL_STR("id (prior node) is required"));
|
||||||
}
|
}
|
||||||
el_val_t tags_raw = json_get(body, EL_STR("tags"));
|
el_val_t tags_raw = json_get(body, EL_STR("tags"));
|
||||||
el_val_t tags = ({ el_val_t _if_result_17 = 0; if (str_eq(tags_raw, EL_STR(""))) { _if_result_17 = (EL_STR("[\"Knowledge\",\"tier:canonical\",\"disposition:stable\"]")); } else { _if_result_17 = (tags_raw); } _if_result_17; });
|
el_val_t tags = ({ el_val_t _if_result_35 = 0; if (str_eq(tags_raw, EL_STR(""))) { _if_result_35 = (EL_STR("[\"Knowledge\",\"tier:canonical\",\"disposition:stable\"]")); } else { _if_result_35 = (tags_raw); } _if_result_35; });
|
||||||
el_val_t new_id = engram_node_full(content, EL_STR("Knowledge"), EL_STR("knowledge:canonical"), el_from_float(el_from_float(0.9)), el_from_float(el_from_float(0.9)), el_from_float(el_from_float(1.0)), EL_STR("Canonical"), tags);
|
el_val_t new_id = engram_node_full(content, EL_STR("Knowledge"), EL_STR("knowledge:canonical"), el_from_float(0.9), el_from_float(0.9), el_from_float(1.0), EL_STR("Canonical"), tags);
|
||||||
if (str_eq(new_id, EL_STR(""))) {
|
if (!api_persisted(new_id)) {
|
||||||
return api_err(EL_STR("failed to create canonical node"));
|
return api_not_persisted(new_id);
|
||||||
}
|
}
|
||||||
engram_connect(new_id, prior_id, el_from_float(el_from_float(0.95)), EL_STR("supersedes"));
|
engram_connect(new_id, prior_id, el_from_float(0.95), EL_STR("supersedes"));
|
||||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"new_id\":\""), new_id), EL_STR("\",\"supersedes\":\"")), prior_id), EL_STR("\"}"));
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"new_id\":\""), new_id), EL_STR("\",\"supersedes\":\"")), prior_id), EL_STR("\"}"));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
el_val_t handle_api_browse_processes(el_val_t method, el_val_t path, el_val_t body) {
|
el_val_t handle_api_browse_processes(el_val_t method, el_val_t path, el_val_t body) {
|
||||||
el_val_t name = ({ el_val_t _if_result_18 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_18 = (api_query_param(path, EL_STR("name"))); } else { _if_result_18 = (json_get(body, EL_STR("name"))); } _if_result_18; });
|
el_val_t name = ({ el_val_t _if_result_36 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_36 = (api_query_param(path, EL_STR("name"))); } else { _if_result_36 = (json_get(body, EL_STR("name"))); } _if_result_36; });
|
||||||
el_val_t limit = api_query_int(path, EL_STR("limit"), 50);
|
el_val_t limit = api_query_int(path, EL_STR("limit"), 50);
|
||||||
if (str_eq(name, EL_STR(""))) {
|
if (str_eq(name, EL_STR(""))) {
|
||||||
return api_or_empty(engram_scan_nodes_by_type_json(EL_STR("Process"), limit, 0));
|
return api_or_empty(engram_scan_nodes_by_type_json(EL_STR("Process"), limit, 0));
|
||||||
@@ -415,9 +411,12 @@ el_val_t handle_api_define_process(el_val_t body) {
|
|||||||
if (str_eq(content, EL_STR(""))) {
|
if (str_eq(content, EL_STR(""))) {
|
||||||
return api_err(EL_STR("content is required"));
|
return api_err(EL_STR("content is required"));
|
||||||
}
|
}
|
||||||
el_val_t label = ({ el_val_t _if_result_19 = 0; if (str_eq(name, EL_STR(""))) { _if_result_19 = (EL_STR("process:unnamed")); } else { _if_result_19 = (el_str_concat(EL_STR("process:"), name)); } _if_result_19; });
|
el_val_t label = ({ el_val_t _if_result_37 = 0; if (str_eq(name, EL_STR(""))) { _if_result_37 = (EL_STR("process:unnamed")); } else { _if_result_37 = (el_str_concat(EL_STR("process:"), name)); } _if_result_37; });
|
||||||
el_val_t tags = EL_STR("[\"Process\"]");
|
el_val_t tags = EL_STR("[\"Process\"]");
|
||||||
el_val_t id = engram_node_full(content, EL_STR("Process"), label, el_from_float(el_from_float(0.8)), el_from_float(el_from_float(0.8)), el_from_float(el_from_float(0.9)), EL_STR("Canonical"), tags);
|
el_val_t id = engram_node_full(content, EL_STR("Process"), label, el_from_float(0.8), el_from_float(0.8), el_from_float(0.9), EL_STR("Canonical"), tags);
|
||||||
|
if (!api_persisted(id)) {
|
||||||
|
return api_not_persisted(id);
|
||||||
|
}
|
||||||
return el_str_concat(el_str_concat(EL_STR("{\"id\":\""), id), EL_STR("\",\"ok\":true}"));
|
return el_str_concat(el_str_concat(EL_STR("{\"id\":\""), id), EL_STR("\",\"ok\":true}"));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -430,22 +429,25 @@ el_val_t handle_api_log_state_event(el_val_t body) {
|
|||||||
el_val_t gap = json_get(body, EL_STR("gap_direction"));
|
el_val_t gap = json_get(body, EL_STR("gap_direction"));
|
||||||
el_val_t legacy = json_get(body, EL_STR("content"));
|
el_val_t legacy = json_get(body, EL_STR("content"));
|
||||||
el_val_t parts = EL_STR("INTERNAL STATE EVENT");
|
el_val_t parts = EL_STR("INTERNAL STATE EVENT");
|
||||||
parts = ({ el_val_t _if_result_20 = 0; if (!str_eq(trigger, EL_STR(""))) { _if_result_20 = (el_str_concat(el_str_concat(parts, EL_STR("\nTrigger: ")), trigger)); } else { _if_result_20 = (parts); } _if_result_20; });
|
parts = ({ el_val_t _if_result_38 = 0; if (!str_eq(trigger, EL_STR(""))) { _if_result_38 = (el_str_concat(el_str_concat(parts, EL_STR("\nTrigger: ")), trigger)); } else { _if_result_38 = (parts); } _if_result_38; });
|
||||||
parts = ({ el_val_t _if_result_21 = 0; if (!str_eq(pre, EL_STR(""))) { _if_result_21 = (el_str_concat(el_str_concat(parts, EL_STR("\nPre-reasoning: ")), pre)); } else { _if_result_21 = (parts); } _if_result_21; });
|
parts = ({ el_val_t _if_result_39 = 0; if (!str_eq(pre, EL_STR(""))) { _if_result_39 = (el_str_concat(el_str_concat(parts, EL_STR("\nPre-reasoning: ")), pre)); } else { _if_result_39 = (parts); } _if_result_39; });
|
||||||
parts = ({ el_val_t _if_result_22 = 0; if (!str_eq(post, EL_STR(""))) { _if_result_22 = (el_str_concat(el_str_concat(parts, EL_STR("\nPost-reasoning: ")), post)); } else { _if_result_22 = (parts); } _if_result_22; });
|
parts = ({ el_val_t _if_result_40 = 0; if (!str_eq(post, EL_STR(""))) { _if_result_40 = (el_str_concat(el_str_concat(parts, EL_STR("\nPost-reasoning: ")), post)); } else { _if_result_40 = (parts); } _if_result_40; });
|
||||||
parts = ({ el_val_t _if_result_23 = 0; if (!str_eq(ratio, EL_STR(""))) { _if_result_23 = (el_str_concat(el_str_concat(parts, EL_STR("\nCompression-ratio: ")), ratio)); } else { _if_result_23 = (parts); } _if_result_23; });
|
parts = ({ el_val_t _if_result_41 = 0; if (!str_eq(ratio, EL_STR(""))) { _if_result_41 = (el_str_concat(el_str_concat(parts, EL_STR("\nCompression-ratio: ")), ratio)); } else { _if_result_41 = (parts); } _if_result_41; });
|
||||||
parts = ({ el_val_t _if_result_24 = 0; if (!str_eq(gap, EL_STR(""))) { _if_result_24 = (el_str_concat(el_str_concat(parts, EL_STR("\nGap-direction: ")), gap)); } else { _if_result_24 = (parts); } _if_result_24; });
|
parts = ({ el_val_t _if_result_42 = 0; if (!str_eq(gap, EL_STR(""))) { _if_result_42 = (el_str_concat(el_str_concat(parts, EL_STR("\nGap-direction: ")), gap)); } else { _if_result_42 = (parts); } _if_result_42; });
|
||||||
parts = ({ el_val_t _if_result_25 = 0; if (!str_eq(legacy, EL_STR(""))) { _if_result_25 = (el_str_concat(el_str_concat(parts, EL_STR("\n")), legacy)); } else { _if_result_25 = (parts); } _if_result_25; });
|
parts = ({ el_val_t _if_result_43 = 0; if (!str_eq(legacy, EL_STR(""))) { _if_result_43 = (el_str_concat(el_str_concat(parts, EL_STR("\n")), legacy)); } else { _if_result_43 = (parts); } _if_result_43; });
|
||||||
el_val_t ts = time_now();
|
el_val_t ts = time_now();
|
||||||
el_val_t boot = state_get(EL_STR("soul_boot_count"));
|
el_val_t boot = state_get(EL_STR("soul_boot_count"));
|
||||||
el_val_t tags = EL_STR("[\"internal-state\",\"InternalStateEvent\",\"pre-reasoning\"]");
|
el_val_t tags = EL_STR("[\"internal-state\",\"InternalStateEvent\",\"pre-reasoning\"]");
|
||||||
el_val_t id = engram_node_full(parts, EL_STR("InternalStateEvent"), EL_STR("state-event:manual"), el_from_float(el_from_float(0.85)), el_from_float(el_from_float(0.85)), el_from_float(el_from_float(0.9)), EL_STR("Episodic"), tags);
|
el_val_t id = engram_node_full(parts, EL_STR("InternalStateEvent"), EL_STR("state-event:manual"), el_from_float(0.85), el_from_float(0.85), el_from_float(0.9), EL_STR("Episodic"), tags);
|
||||||
|
if (!api_persisted(id)) {
|
||||||
|
return api_not_persisted(id);
|
||||||
|
}
|
||||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"id\":\""), id), EL_STR("\",\"boot\":\"")), boot), EL_STR("\"}"));
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"id\":\""), id), EL_STR("\",\"boot\":\"")), boot), EL_STR("\"}"));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
el_val_t handle_api_list_state_events(el_val_t method, el_val_t path, el_val_t body) {
|
el_val_t handle_api_list_state_events(el_val_t method, el_val_t path, el_val_t body) {
|
||||||
el_val_t q = ({ el_val_t _if_result_26 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_26 = (api_query_param(path, EL_STR("query"))); } else { _if_result_26 = (json_get(body, EL_STR("query"))); } _if_result_26; });
|
el_val_t q = ({ el_val_t _if_result_44 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_44 = (api_query_param(path, EL_STR("query"))); } else { _if_result_44 = (json_get(body, EL_STR("query"))); } _if_result_44; });
|
||||||
el_val_t limit = api_query_int(path, EL_STR("limit"), 20);
|
el_val_t limit = api_query_int(path, EL_STR("limit"), 20);
|
||||||
if (!str_eq(q, EL_STR(""))) {
|
if (!str_eq(q, EL_STR(""))) {
|
||||||
return api_or_empty(engram_search_json(el_str_concat(EL_STR("internal state "), q), limit));
|
return api_or_empty(engram_search_json(el_str_concat(EL_STR("internal state "), q), limit));
|
||||||
@@ -456,7 +458,7 @@ el_val_t handle_api_list_state_events(el_val_t method, el_val_t path, el_val_t b
|
|||||||
|
|
||||||
el_val_t handle_api_inspect_config(el_val_t path, el_val_t body) {
|
el_val_t handle_api_inspect_config(el_val_t path, el_val_t body) {
|
||||||
el_val_t key = api_query_param(path, EL_STR("key"));
|
el_val_t key = api_query_param(path, EL_STR("key"));
|
||||||
key = ({ el_val_t _if_result_27 = 0; if (str_eq(key, EL_STR(""))) { _if_result_27 = (json_get(body, EL_STR("key"))); } else { _if_result_27 = (key); } _if_result_27; });
|
key = ({ el_val_t _if_result_45 = 0; if (str_eq(key, EL_STR(""))) { _if_result_45 = (json_get(body, EL_STR("key"))); } else { _if_result_45 = (key); } _if_result_45; });
|
||||||
if (str_eq(key, EL_STR(""))) {
|
if (str_eq(key, EL_STR(""))) {
|
||||||
return EL_STR("{\"hint\":\"pass ?key=<name>\",\"known\":[\"neuron.self.traversal_root\",\"neuron.self.values_hub\"]}");
|
return EL_STR("{\"hint\":\"pass ?key=<name>\",\"known\":[\"neuron.self.traversal_root\",\"neuron.self.values_hub\"]}");
|
||||||
}
|
}
|
||||||
@@ -473,7 +475,7 @@ el_val_t handle_api_inspect_config(el_val_t path, el_val_t body) {
|
|||||||
el_val_t node = json_array_get(results, 0);
|
el_val_t node = json_array_get(results, 0);
|
||||||
el_val_t content = json_get(node, EL_STR("content"));
|
el_val_t content = json_get(node, EL_STR("content"));
|
||||||
el_val_t prefix = el_str_concat(el_str_concat(EL_STR("config:"), key), EL_STR("="));
|
el_val_t prefix = el_str_concat(el_str_concat(EL_STR("config:"), key), EL_STR("="));
|
||||||
el_val_t value = ({ el_val_t _if_result_28 = 0; if (str_starts_with(content, prefix)) { _if_result_28 = (str_slice(content, str_len(prefix), str_len(content))); } else { _if_result_28 = (content); } _if_result_28; });
|
el_val_t value = ({ el_val_t _if_result_46 = 0; if (str_starts_with(content, prefix)) { _if_result_46 = (str_slice(content, str_len(prefix), str_len(content))); } else { _if_result_46 = (content); } _if_result_46; });
|
||||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"key\":\""), key), EL_STR("\",\"value\":\"")), value), EL_STR("\"}"));
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"key\":\""), key), EL_STR("\",\"value\":\"")), value), EL_STR("\"}"));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -486,19 +488,22 @@ el_val_t handle_api_tune_config(el_val_t body) {
|
|||||||
}
|
}
|
||||||
el_val_t content = el_str_concat(el_str_concat(el_str_concat(EL_STR("config:"), key), EL_STR("=")), value);
|
el_val_t content = el_str_concat(el_str_concat(el_str_concat(EL_STR("config:"), key), EL_STR("=")), value);
|
||||||
el_val_t tags = EL_STR("[\"ConfigEntry\",\"config\"]");
|
el_val_t tags = EL_STR("[\"ConfigEntry\",\"config\"]");
|
||||||
el_val_t id = engram_node_full(content, EL_STR("ConfigEntry"), key, el_from_float(el_from_float(0.85)), el_from_float(el_from_float(0.85)), el_from_float(el_from_float(0.9)), EL_STR("Canonical"), tags);
|
el_val_t id = engram_node_full(content, EL_STR("ConfigEntry"), key, el_from_float(0.85), el_from_float(0.85), el_from_float(0.9), EL_STR("Canonical"), tags);
|
||||||
|
if (!api_persisted(id)) {
|
||||||
|
return api_not_persisted(id);
|
||||||
|
}
|
||||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"key\":\""), key), EL_STR("\",\"value\":\"")), value), EL_STR("\",\"id\":\"")), id), EL_STR("\"}"));
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"key\":\""), key), EL_STR("\",\"value\":\"")), value), EL_STR("\",\"id\":\"")), id), EL_STR("\"}"));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
el_val_t handle_api_inspect_graph(el_val_t method, el_val_t path, el_val_t body) {
|
el_val_t handle_api_inspect_graph(el_val_t method, el_val_t path, el_val_t body) {
|
||||||
el_val_t entity_id = ({ el_val_t _if_result_29 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_29 = (api_query_param(path, EL_STR("id"))); } else { _if_result_29 = (json_get(body, EL_STR("entity_id"))); } _if_result_29; });
|
el_val_t entity_id = ({ el_val_t _if_result_47 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_47 = (api_query_param(path, EL_STR("id"))); } else { _if_result_47 = (json_get(body, EL_STR("entity_id"))); } _if_result_47; });
|
||||||
el_val_t name = ({ el_val_t _if_result_30 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_30 = (api_query_param(path, EL_STR("name"))); } else { _if_result_30 = (json_get(body, EL_STR("name"))); } _if_result_30; });
|
el_val_t name = ({ el_val_t _if_result_48 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_48 = (api_query_param(path, EL_STR("name"))); } else { _if_result_48 = (json_get(body, EL_STR("name"))); } _if_result_48; });
|
||||||
el_val_t depth = api_query_int(path, EL_STR("depth"), 0);
|
el_val_t depth = api_query_int(path, EL_STR("depth"), 0);
|
||||||
depth = ({ el_val_t _if_result_31 = 0; if ((depth == 0)) { _if_result_31 = (json_get_int(body, EL_STR("max_depth"))); } else { _if_result_31 = (depth); } _if_result_31; });
|
depth = ({ el_val_t _if_result_49 = 0; if ((depth == 0)) { _if_result_49 = (json_get_int(body, EL_STR("max_depth"))); } else { _if_result_49 = (depth); } _if_result_49; });
|
||||||
depth = ({ el_val_t _if_result_32 = 0; if ((depth == 0)) { _if_result_32 = (1); } else { _if_result_32 = (depth); } _if_result_32; });
|
depth = ({ el_val_t _if_result_50 = 0; if ((depth == 0)) { _if_result_50 = (1); } else { _if_result_50 = (depth); } _if_result_50; });
|
||||||
el_val_t resolved = entity_id;
|
el_val_t resolved = entity_id;
|
||||||
resolved = ({ el_val_t _if_result_33 = 0; if (str_eq(resolved, EL_STR(""))) { _if_result_33 = (({ el_val_t _if_result_34 = 0; if ((str_eq(name, EL_STR("self")) || str_eq(name, EL_STR("neuron")))) { _if_result_34 = (EL_STR("kn-efeb4a5b-5aff-4759-8a97-7233099be6ee")); } else { _if_result_34 = (({ el_val_t _if_result_35 = 0; if ((str_eq(name, EL_STR("values")) || str_eq(name, EL_STR("values_hub")))) { _if_result_35 = (EL_STR("kn-5b606390-a52d-4ca2-8e0e-eba141d13440")); } else { _if_result_35 = (EL_STR("")); } _if_result_35; })); } _if_result_34; })); } else { _if_result_33 = (resolved); } _if_result_33; });
|
resolved = ({ el_val_t _if_result_51 = 0; if (str_eq(resolved, EL_STR(""))) { _if_result_51 = (({ el_val_t _if_result_52 = 0; if ((str_eq(name, EL_STR("self")) || str_eq(name, EL_STR("neuron")))) { _if_result_52 = (EL_STR("kn-efeb4a5b-5aff-4759-8a97-7233099be6ee")); } else { _if_result_52 = (({ el_val_t _if_result_53 = 0; if ((str_eq(name, EL_STR("values")) || str_eq(name, EL_STR("values_hub")))) { _if_result_53 = (EL_STR("kn-5b606390-a52d-4ca2-8e0e-eba141d13440")); } else { _if_result_53 = (EL_STR("")); } _if_result_53; })); } _if_result_52; })); } else { _if_result_51 = (resolved); } _if_result_51; });
|
||||||
if (str_eq(resolved, EL_STR(""))) {
|
if (str_eq(resolved, EL_STR(""))) {
|
||||||
return api_err(EL_STR("entity_id or name required. Known names: self, neuron, values, values_hub"));
|
return api_err(EL_STR("entity_id or name required. Known names: self, neuron, values, values_hub"));
|
||||||
}
|
}
|
||||||
@@ -520,8 +525,8 @@ el_val_t handle_api_link_entities(el_val_t body) {
|
|||||||
return api_err_protected(to_id);
|
return api_err_protected(to_id);
|
||||||
}
|
}
|
||||||
el_val_t relation = json_get(body, EL_STR("relation"));
|
el_val_t relation = json_get(body, EL_STR("relation"));
|
||||||
el_val_t eff_relation = ({ el_val_t _if_result_36 = 0; if (str_eq(relation, EL_STR(""))) { _if_result_36 = (EL_STR("associates")); } else { _if_result_36 = (relation); } _if_result_36; });
|
el_val_t eff_relation = ({ el_val_t _if_result_54 = 0; if (str_eq(relation, EL_STR(""))) { _if_result_54 = (EL_STR("associates")); } else { _if_result_54 = (relation); } _if_result_54; });
|
||||||
engram_connect(from_id, to_id, el_from_float(el_from_float(0.5)), eff_relation);
|
engram_connect(from_id, to_id, el_from_float(0.5), eff_relation);
|
||||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"from_id\":\""), from_id), EL_STR("\",\"to_id\":\"")), to_id), EL_STR("\",\"relation\":\"")), eff_relation), EL_STR("\"}"));
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"from_id\":\""), from_id), EL_STR("\",\"to_id\":\"")), to_id), EL_STR("\",\"relation\":\"")), eff_relation), EL_STR("\"}"));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -549,17 +554,54 @@ el_val_t handle_api_evolve_memory(el_val_t body) {
|
|||||||
return api_err_protected(prior_id);
|
return api_err_protected(prior_id);
|
||||||
}
|
}
|
||||||
el_val_t importance = json_get(body, EL_STR("importance"));
|
el_val_t importance = json_get(body, EL_STR("importance"));
|
||||||
el_val_t sal_str = ({ el_val_t _if_result_37 = 0; if (str_eq(importance, EL_STR("critical"))) { _if_result_37 = (EL_STR("0.95")); } else { _if_result_37 = (({ el_val_t _if_result_38 = 0; if (str_eq(importance, EL_STR("high"))) { _if_result_38 = (EL_STR("0.75")); } else { _if_result_38 = (({ el_val_t _if_result_39 = 0; if (str_eq(importance, EL_STR("low"))) { _if_result_39 = (EL_STR("0.25")); } else { _if_result_39 = (EL_STR("0.50")); } _if_result_39; })); } _if_result_38; })); } _if_result_37; });
|
el_val_t sal_str = ({ el_val_t _if_result_55 = 0; if (str_eq(importance, EL_STR("critical"))) { _if_result_55 = (EL_STR("0.95")); } else { _if_result_55 = (({ el_val_t _if_result_56 = 0; if (str_eq(importance, EL_STR("high"))) { _if_result_56 = (EL_STR("0.75")); } else { _if_result_56 = (({ el_val_t _if_result_57 = 0; if (str_eq(importance, EL_STR("low"))) { _if_result_57 = (EL_STR("0.25")); } else { _if_result_57 = (EL_STR("0.50")); } _if_result_57; })); } _if_result_56; })); } _if_result_55; });
|
||||||
el_val_t sal = ({ el_val_t _if_result_40 = 0; if (str_eq(sal_str, EL_STR("0.95"))) { _if_result_40 = (el_from_float(0.95)); } else { _if_result_40 = (({ el_val_t _if_result_41 = 0; if (str_eq(sal_str, EL_STR("0.75"))) { _if_result_41 = (el_from_float(0.75)); } else { _if_result_41 = (({ el_val_t _if_result_42 = 0; if (str_eq(sal_str, EL_STR("0.25"))) { _if_result_42 = (el_from_float(0.25)); } else { _if_result_42 = (el_from_float(0.5)); } _if_result_42; })); } _if_result_41; })); } _if_result_40; });
|
el_val_t sal = ({ el_val_t _if_result_58 = 0; if (str_eq(sal_str, EL_STR("0.95"))) { _if_result_58 = (el_from_float(0.95)); } else { _if_result_58 = (({ el_val_t _if_result_59 = 0; if (str_eq(sal_str, EL_STR("0.75"))) { _if_result_59 = (el_from_float(0.75)); } else { _if_result_59 = (({ el_val_t _if_result_60 = 0; if (str_eq(sal_str, EL_STR("0.25"))) { _if_result_60 = (el_from_float(0.25)); } else { _if_result_60 = (el_from_float(0.5)); } _if_result_60; })); } _if_result_59; })); } _if_result_58; });
|
||||||
el_val_t tags = EL_STR("[\"Memory\",\"evolved\"]");
|
el_val_t tags = EL_STR("[\"Memory\",\"evolved\"]");
|
||||||
el_val_t new_id = engram_node_full(content, EL_STR("Memory"), EL_STR("memory:evolved"), el_from_float(sal), el_from_float(sal), el_from_float(el_from_float(0.9)), EL_STR("Episodic"), tags);
|
el_val_t new_id = engram_node_full(content, EL_STR("Memory"), EL_STR("memory:evolved"), el_from_float(sal), el_from_float(sal), el_from_float(0.9), EL_STR("Episodic"), tags);
|
||||||
if (!str_eq(prior_id, EL_STR("")) && !str_eq(new_id, EL_STR(""))) {
|
if (!str_eq(prior_id, EL_STR("")) && !str_eq(new_id, EL_STR(""))) {
|
||||||
engram_connect(new_id, prior_id, el_from_float(el_from_float(0.9)), EL_STR("supersedes"));
|
engram_connect(new_id, prior_id, el_from_float(0.9), EL_STR("supersedes"));
|
||||||
}
|
}
|
||||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"id\":\""), new_id), EL_STR("\",\"supersedes\":\"")), prior_id), EL_STR("\",\"ok\":true}"));
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"id\":\""), new_id), EL_STR("\",\"supersedes\":\"")), prior_id), EL_STR("\",\"ok\":true}"));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
el_val_t handle_api_memory_delete(el_val_t body) {
|
||||||
|
el_val_t node_id = json_get(body, EL_STR("id"));
|
||||||
|
if (str_eq(node_id, EL_STR(""))) {
|
||||||
|
return api_err(EL_STR("id is required"));
|
||||||
|
}
|
||||||
|
if (is_protected_node(node_id)) {
|
||||||
|
return api_err_protected(node_id);
|
||||||
|
}
|
||||||
|
el_val_t existing = engram_get_node_json(node_id);
|
||||||
|
if (str_eq(existing, EL_STR("{}"))) {
|
||||||
|
return api_err(el_str_concat(EL_STR("memory not found: "), node_id));
|
||||||
|
}
|
||||||
|
mem_forget(node_id);
|
||||||
|
return el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"id\":\""), node_id), EL_STR("\",\"deleted\":true}"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
el_val_t handle_api_memory_update(el_val_t body) {
|
||||||
|
el_val_t prior_id = json_get(body, EL_STR("id"));
|
||||||
|
el_val_t content = json_get(body, EL_STR("content"));
|
||||||
|
if (str_eq(prior_id, EL_STR(""))) {
|
||||||
|
return api_err(EL_STR("id is required"));
|
||||||
|
}
|
||||||
|
if (str_eq(content, EL_STR(""))) {
|
||||||
|
return api_err(EL_STR("content is required"));
|
||||||
|
}
|
||||||
|
if (is_protected_node(prior_id)) {
|
||||||
|
return api_err_protected(prior_id);
|
||||||
|
}
|
||||||
|
el_val_t existing = engram_get_node_json(prior_id);
|
||||||
|
if (str_eq(existing, EL_STR("{}"))) {
|
||||||
|
return api_err(el_str_concat(EL_STR("memory not found: "), prior_id));
|
||||||
|
}
|
||||||
|
return handle_api_evolve_memory(body);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
el_val_t handle_api_cultivate(el_val_t body) {
|
el_val_t handle_api_cultivate(el_val_t body) {
|
||||||
el_val_t op = json_get(body, EL_STR("operation"));
|
el_val_t op = json_get(body, EL_STR("operation"));
|
||||||
if (str_eq(op, EL_STR(""))) {
|
if (str_eq(op, EL_STR(""))) {
|
||||||
@@ -572,9 +614,9 @@ el_val_t handle_api_cultivate(el_val_t body) {
|
|||||||
return api_err(EL_STR("content is required"));
|
return api_err(EL_STR("content is required"));
|
||||||
}
|
}
|
||||||
el_val_t tags = EL_STR("[\"Knowledge\",\"evolved\",\"cultivated\"]");
|
el_val_t tags = EL_STR("[\"Knowledge\",\"evolved\",\"cultivated\"]");
|
||||||
el_val_t new_id = engram_node_full(content, EL_STR("Knowledge"), EL_STR("knowledge:cultivated"), el_from_float(el_from_float(0.75)), el_from_float(el_from_float(0.75)), el_from_float(el_from_float(0.9)), EL_STR("Episodic"), tags);
|
el_val_t new_id = engram_node_full(content, EL_STR("Knowledge"), EL_STR("knowledge:cultivated"), el_from_float(0.75), el_from_float(0.75), el_from_float(0.9), EL_STR("Episodic"), tags);
|
||||||
if (!str_eq(prior_id, EL_STR("")) && !str_eq(new_id, EL_STR(""))) {
|
if (!str_eq(prior_id, EL_STR("")) && !str_eq(new_id, EL_STR(""))) {
|
||||||
engram_connect(new_id, prior_id, el_from_float(el_from_float(0.9)), EL_STR("supersedes"));
|
engram_connect(new_id, prior_id, el_from_float(0.9), EL_STR("supersedes"));
|
||||||
}
|
}
|
||||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"id\":\""), new_id), EL_STR("\",\"supersedes\":\"")), prior_id), EL_STR("\",\"ok\":true,\"cultivated\":true}"));
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"id\":\""), new_id), EL_STR("\",\"supersedes\":\"")), prior_id), EL_STR("\",\"ok\":true,\"cultivated\":true}"));
|
||||||
}
|
}
|
||||||
@@ -585,11 +627,11 @@ el_val_t handle_api_cultivate(el_val_t body) {
|
|||||||
return api_err(EL_STR("content is required"));
|
return api_err(EL_STR("content is required"));
|
||||||
}
|
}
|
||||||
el_val_t importance = json_get(body, EL_STR("importance"));
|
el_val_t importance = json_get(body, EL_STR("importance"));
|
||||||
el_val_t sal = ({ el_val_t _if_result_43 = 0; if (str_eq(importance, EL_STR("critical"))) { _if_result_43 = (el_from_float(0.95)); } else { _if_result_43 = (({ el_val_t _if_result_44 = 0; if (str_eq(importance, EL_STR("high"))) { _if_result_44 = (el_from_float(0.75)); } else { _if_result_44 = (({ el_val_t _if_result_45 = 0; if (str_eq(importance, EL_STR("low"))) { _if_result_45 = (el_from_float(0.25)); } else { _if_result_45 = (el_from_float(0.5)); } _if_result_45; })); } _if_result_44; })); } _if_result_43; });
|
el_val_t sal = ({ el_val_t _if_result_61 = 0; if (str_eq(importance, EL_STR("critical"))) { _if_result_61 = (el_from_float(0.95)); } else { _if_result_61 = (({ el_val_t _if_result_62 = 0; if (str_eq(importance, EL_STR("high"))) { _if_result_62 = (el_from_float(0.75)); } else { _if_result_62 = (({ el_val_t _if_result_63 = 0; if (str_eq(importance, EL_STR("low"))) { _if_result_63 = (el_from_float(0.25)); } else { _if_result_63 = (el_from_float(0.5)); } _if_result_63; })); } _if_result_62; })); } _if_result_61; });
|
||||||
el_val_t tags = EL_STR("[\"Memory\",\"evolved\",\"cultivated\"]");
|
el_val_t tags = EL_STR("[\"Memory\",\"evolved\",\"cultivated\"]");
|
||||||
el_val_t new_id = engram_node_full(content, EL_STR("Memory"), EL_STR("memory:cultivated"), el_from_float(sal), el_from_float(sal), el_from_float(el_from_float(0.9)), EL_STR("Episodic"), tags);
|
el_val_t new_id = engram_node_full(content, EL_STR("Memory"), EL_STR("memory:cultivated"), el_from_float(sal), el_from_float(sal), el_from_float(0.9), EL_STR("Episodic"), tags);
|
||||||
if (!str_eq(prior_id, EL_STR("")) && !str_eq(new_id, EL_STR(""))) {
|
if (!str_eq(prior_id, EL_STR("")) && !str_eq(new_id, EL_STR(""))) {
|
||||||
engram_connect(new_id, prior_id, el_from_float(el_from_float(0.9)), EL_STR("supersedes"));
|
engram_connect(new_id, prior_id, el_from_float(0.9), EL_STR("supersedes"));
|
||||||
}
|
}
|
||||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"id\":\""), new_id), EL_STR("\",\"supersedes\":\"")), prior_id), EL_STR("\",\"ok\":true,\"cultivated\":true}"));
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"id\":\""), new_id), EL_STR("\",\"supersedes\":\"")), prior_id), EL_STR("\",\"ok\":true,\"cultivated\":true}"));
|
||||||
}
|
}
|
||||||
@@ -611,8 +653,8 @@ el_val_t handle_api_cultivate(el_val_t body) {
|
|||||||
return api_err(EL_STR("to_id is required"));
|
return api_err(EL_STR("to_id is required"));
|
||||||
}
|
}
|
||||||
el_val_t relation = json_get(body, EL_STR("relation"));
|
el_val_t relation = json_get(body, EL_STR("relation"));
|
||||||
el_val_t eff_relation = ({ el_val_t _if_result_46 = 0; if (str_eq(relation, EL_STR(""))) { _if_result_46 = (EL_STR("associates")); } else { _if_result_46 = (relation); } _if_result_46; });
|
el_val_t eff_relation = ({ el_val_t _if_result_64 = 0; if (str_eq(relation, EL_STR(""))) { _if_result_64 = (EL_STR("associates")); } else { _if_result_64 = (relation); } _if_result_64; });
|
||||||
engram_connect(from_id, to_id, el_from_float(el_from_float(0.5)), eff_relation);
|
engram_connect(from_id, to_id, el_from_float(0.5), eff_relation);
|
||||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"from_id\":\""), from_id), EL_STR("\",\"to_id\":\"")), to_id), EL_STR("\",\"relation\":\"")), eff_relation), EL_STR("\",\"cultivated\":true}"));
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"from_id\":\""), from_id), EL_STR("\",\"to_id\":\"")), to_id), EL_STR("\",\"relation\":\"")), eff_relation), EL_STR("\",\"cultivated\":true}"));
|
||||||
}
|
}
|
||||||
return api_err(el_str_concat(el_str_concat(EL_STR("unknown operation: "), op), EL_STR(" (valid: evolve_knowledge, evolve_memory, forget, link_entities)")));
|
return api_err(el_str_concat(el_str_concat(EL_STR("unknown operation: "), op), EL_STR(" (valid: evolve_knowledge, evolve_memory, forget, link_entities)")));
|
||||||
@@ -629,19 +671,20 @@ el_val_t handle_api_consolidate(el_val_t body) {
|
|||||||
el_val_t summary = json_get(body, EL_STR("summary"));
|
el_val_t summary = json_get(body, EL_STR("summary"));
|
||||||
el_val_t snap = state_get(EL_STR("soul_snapshot_path"));
|
el_val_t snap = state_get(EL_STR("soul_snapshot_path"));
|
||||||
if (!str_eq(snap, EL_STR(""))) {
|
if (!str_eq(snap, EL_STR(""))) {
|
||||||
engram_save(snap);
|
el_val_t save_result = engram_save(snap);
|
||||||
|
if (str_eq(save_result, EL_STR(""))) {
|
||||||
|
println(el_str_concat(el_str_concat(EL_STR("[api] consolidate: engram_save failed for "), snap), EL_STR(" \xe2\x80\x94 snapshot may be out of sync")));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!str_eq(summary, EL_STR(""))) {
|
if (!str_eq(summary, EL_STR(""))) {
|
||||||
el_val_t safe_summary = str_replace(summary, EL_STR("\""), EL_STR("'"));
|
el_val_t safe_summary = str_replace(summary, EL_STR("\""), EL_STR("'"));
|
||||||
el_val_t tags = EL_STR("[\"SessionSummary\",\"consolidate\"]");
|
el_val_t tags = EL_STR("[\"SessionSummary\",\"consolidate\"]");
|
||||||
el_val_t discard = engram_node_full(el_str_concat(EL_STR("[session-summary] "), safe_summary), EL_STR("SessionSummary"), EL_STR("session:summary"), el_from_float(el_from_float(0.7)), el_from_float(el_from_float(0.7)), el_from_float(el_from_float(0.9)), EL_STR("Episodic"), tags);
|
el_val_t summary_id = engram_node_full(el_str_concat(EL_STR("[session-summary] "), safe_summary), EL_STR("SessionSummary"), EL_STR("session:summary"), el_from_float(0.7), el_from_float(0.7), el_from_float(0.9), EL_STR("Episodic"), tags);
|
||||||
|
if (str_eq(summary_id, EL_STR(""))) {
|
||||||
|
println(EL_STR("[api] consolidate: session summary engram write failed \xe2\x80\x94 summary node lost"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"snapshot\":\""), snap), EL_STR("\"}"));
|
return el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"snapshot\":\""), snap), EL_STR("\"}"));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int _argc, char** _argv) {
|
|
||||||
el_runtime_init_args(_argc, _argv);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
+7
@@ -8,9 +8,14 @@ extern fn api_ok(extra: String) -> String
|
|||||||
extern fn api_err(msg: String) -> String
|
extern fn api_err(msg: String) -> String
|
||||||
extern fn api_nonempty(s: String) -> Bool
|
extern fn api_nonempty(s: String) -> Bool
|
||||||
extern fn api_or_empty(s: String) -> String
|
extern fn api_or_empty(s: String) -> String
|
||||||
|
extern fn api_persisted(id: String) -> Bool
|
||||||
|
extern fn api_not_persisted(id: String) -> String
|
||||||
extern fn handle_api_begin_session(body: String) -> String
|
extern fn handle_api_begin_session(body: String) -> String
|
||||||
extern fn handle_api_compile_ctx(body: String) -> String
|
extern fn handle_api_compile_ctx(body: String) -> String
|
||||||
extern fn handle_api_remember(body: String) -> String
|
extern fn handle_api_remember(body: String) -> String
|
||||||
|
extern fn handle_api_node_create(body: String) -> String
|
||||||
|
extern fn handle_api_node_delete(body: String) -> String
|
||||||
|
extern fn handle_api_node_update(body: String) -> String
|
||||||
extern fn handle_api_recall(method: String, path: String, body: String) -> String
|
extern fn handle_api_recall(method: String, path: String, body: String) -> String
|
||||||
extern fn handle_api_search_knowledge(method: String, path: String, body: String) -> String
|
extern fn handle_api_search_knowledge(method: String, path: String, body: String) -> String
|
||||||
extern fn handle_api_browse_knowledge(path: String, body: String) -> String
|
extern fn handle_api_browse_knowledge(path: String, body: String) -> String
|
||||||
@@ -27,6 +32,8 @@ extern fn handle_api_inspect_graph(method: String, path: String, body: String) -
|
|||||||
extern fn handle_api_link_entities(body: String) -> String
|
extern fn handle_api_link_entities(body: String) -> String
|
||||||
extern fn handle_api_forget(body: String) -> String
|
extern fn handle_api_forget(body: String) -> String
|
||||||
extern fn handle_api_evolve_memory(body: String) -> String
|
extern fn handle_api_evolve_memory(body: String) -> String
|
||||||
|
extern fn handle_api_memory_delete(body: String) -> String
|
||||||
|
extern fn handle_api_memory_update(body: String) -> String
|
||||||
extern fn handle_api_cultivate(body: String) -> String
|
extern fn handle_api_cultivate(body: String) -> String
|
||||||
extern fn handle_api_list_typed(node_type: String, path: String, body: String) -> String
|
extern fn handle_api_list_typed(node_type: String, path: String, body: String) -> String
|
||||||
extern fn handle_api_consolidate(body: String) -> String
|
extern fn handle_api_consolidate(body: String) -> String
|
||||||
|
|||||||
+259
-28681
File diff suppressed because one or more lines are too long
+2
-7
@@ -193,10 +193,10 @@ el_val_t realize_question_lang(el_val_t predicate, el_val_t tense, el_val_t aspe
|
|||||||
loc_part = core;
|
loc_part = core;
|
||||||
}
|
}
|
||||||
if (str_eq(code, EL_STR("ja"))) {
|
if (str_eq(code, EL_STR("ja"))) {
|
||||||
return el_str_concat(loc_part, EL_STR(" か"));
|
return el_str_concat(loc_part, EL_STR(" \xe3\x81\x8b"));
|
||||||
}
|
}
|
||||||
if (str_eq(code, EL_STR("hi"))) {
|
if (str_eq(code, EL_STR("hi"))) {
|
||||||
return el_str_concat(loc_part, EL_STR(" क्या"));
|
return el_str_concat(loc_part, EL_STR(" \xe0\xa4\x95\xe0\xa5\x8d\xe0\xa4\xaf\xe0\xa4\xbe"));
|
||||||
}
|
}
|
||||||
if (str_eq(code, EL_STR("fi"))) {
|
if (str_eq(code, EL_STR("fi"))) {
|
||||||
return el_str_concat(loc_part, EL_STR("-ko"));
|
return el_str_concat(loc_part, EL_STR("-ko"));
|
||||||
@@ -314,8 +314,3 @@ el_val_t realize(el_val_t form) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int _argc, char** _argv) {
|
|
||||||
el_runtime_init_args(_argc, _argv);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
+5
-5
@@ -1,10 +1,10 @@
|
|||||||
// auto-generated by elc --emit-header - do not edit
|
// auto-generated by elc --emit-header — do not edit
|
||||||
extern fn agent_person(agent: String) -> String
|
extern fn agent_person(agent: String) -> String
|
||||||
extern fn agent_number(agent: String) -> String
|
extern fn agent_number(agent: String) -> String
|
||||||
extern fn realize_np(referent: String, number: String) -> String
|
extern fn realize_np(referent: String, number: String) -> String
|
||||||
extern fn realize_vp_lang(base_verb: String, tense: String, aspect: String, person: String, number: String, profile: Any) -> Any
|
extern fn realize_vp_lang(base_verb: String, tense: String, aspect: String, person: String, number: String, profile: [String]) -> [String]
|
||||||
extern fn realize_question_lang(predicate: String, tense: String, aspect: String, person: String, number: String, agent: String, patient: String, location: String, profile: Any) -> String
|
extern fn realize_question_lang(predicate: String, tense: String, aspect: String, person: String, number: String, agent: String, patient: String, location: String, profile: [String]) -> String
|
||||||
extern fn capitalize_first(s: String) -> String
|
extern fn capitalize_first(s: String) -> String
|
||||||
extern fn add_punct(s: String, intent: String) -> String
|
extern fn add_punct(s: String, intent: String) -> String
|
||||||
extern fn realize_lang(form: Any, profile: Any) -> String
|
extern fn realize_lang(form: [String], profile: [String]) -> String
|
||||||
extern fn realize(form: Any) -> String
|
extern fn realize(form: [String]) -> String
|
||||||
|
|||||||
+218
-27618
File diff suppressed because one or more lines are too long
+4
-3
@@ -1,4 +1,5 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header — do not edit
|
||||||
|
extern fn rate_limit_check(ip: String, path: String) -> String
|
||||||
extern fn strip_query(path: String) -> String
|
extern fn strip_query(path: String) -> String
|
||||||
extern fn err_404(path: String) -> String
|
extern fn err_404(path: String) -> String
|
||||||
extern fn err_405(method: String, path: String) -> String
|
extern fn err_405(method: String, path: String) -> String
|
||||||
@@ -8,7 +9,7 @@ extern fn route_imprint_contextual(body: String) -> String
|
|||||||
extern fn route_imprint_user(body: String) -> String
|
extern fn route_imprint_user(body: String) -> String
|
||||||
extern fn route_synthesize(body: String) -> String
|
extern fn route_synthesize(body: String) -> String
|
||||||
extern fn handle_dharma_recv(body: String) -> String
|
extern fn handle_dharma_recv(body: String) -> String
|
||||||
extern fn route_sessions() -> String
|
extern fn connectd_get(suffix: String) -> String
|
||||||
extern fn parse_session_id_from_path(path: String) -> String
|
extern fn connectd_post(suffix: String, body: String) -> String
|
||||||
extern fn parse_session_subpath(path: String) -> String
|
extern fn handle_connectors(method: String, clean: String, body: String) -> String
|
||||||
extern fn handle_request(method: String, path: String, body: String) -> String
|
extern fn handle_request(method: String, path: String, body: String) -> String
|
||||||
|
|||||||
+55
-110
@@ -27,110 +27,19 @@ el_val_t safety_threat_score(el_val_t input, el_val_t history);
|
|||||||
el_val_t safety_screen(el_val_t input, el_val_t history);
|
el_val_t safety_screen(el_val_t input, el_val_t history);
|
||||||
el_val_t safety_validate(el_val_t output, el_val_t action);
|
el_val_t safety_validate(el_val_t output, el_val_t action);
|
||||||
el_val_t safety_log_bell(el_val_t level, el_val_t reason, el_val_t input_summary);
|
el_val_t safety_log_bell(el_val_t level, el_val_t reason, el_val_t input_summary);
|
||||||
|
el_val_t safety_self_harm_phrases(void);
|
||||||
el_val_t tier_working(void) {
|
el_val_t safety_abuse_phrases(void);
|
||||||
return EL_STR("Working");
|
el_val_t safety_general_hard_phrases(void);
|
||||||
return 0;
|
el_val_t safety_soft_phrases(void);
|
||||||
}
|
el_val_t safety_detect_positive_level(el_val_t message);
|
||||||
|
el_val_t safety_detect_bell_level(el_val_t message);
|
||||||
el_val_t tier_episodic(void) {
|
el_val_t safety_classify_hard_bell(el_val_t message);
|
||||||
return EL_STR("Episodic");
|
el_val_t safety_soft_directive(void);
|
||||||
return 0;
|
el_val_t safety_hard_directive(el_val_t hard_type);
|
||||||
}
|
el_val_t safety_augment_system(el_val_t system, el_val_t user_msg);
|
||||||
|
el_val_t safety_contact_path(void);
|
||||||
el_val_t tier_canonical(void) {
|
el_val_t handle_safety_contact_get(void);
|
||||||
return EL_STR("Canonical");
|
el_val_t handle_safety_contact_post(el_val_t body);
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_store(el_val_t content, el_val_t label, el_val_t tags) {
|
|
||||||
return engram_node_full(content, EL_STR("Memory"), label, el_from_float(el_from_float(0.5)), el_from_float(el_from_float(0.5)), el_from_float(el_from_float(0.8)), EL_STR("Working"), tags);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_remember(el_val_t content, el_val_t tags) {
|
|
||||||
return mem_store(content, EL_STR("soul-memory"), tags);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_recall(el_val_t query, el_val_t depth) {
|
|
||||||
return engram_activate_json(query, depth);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_search(el_val_t query, el_val_t limit) {
|
|
||||||
return engram_search_json(query, limit);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_strengthen(el_val_t node_id) {
|
|
||||||
engram_strengthen(node_id);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_forget(el_val_t node_id) {
|
|
||||||
engram_forget(node_id);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_consolidate(void) {
|
|
||||||
el_val_t scanned = engram_node_count();
|
|
||||||
el_val_t dummy = engram_scan_nodes_json(100, 0);
|
|
||||||
el_val_t total_nodes = engram_node_count();
|
|
||||||
el_val_t total_edges = engram_edge_count();
|
|
||||||
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"scanned\":"), int_to_str(scanned)), EL_STR(",\"total_nodes\":")), int_to_str(total_nodes)), EL_STR(",\"total_edges\":")), int_to_str(total_edges)), EL_STR("}"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_save(el_val_t path) {
|
|
||||||
engram_save(path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_load(el_val_t path) {
|
|
||||||
engram_load(path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_boot_count_get(void) {
|
|
||||||
el_val_t results = engram_search_json(EL_STR("soul:boot_count"), 3);
|
|
||||||
if (str_eq(results, EL_STR(""))) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (str_eq(results, EL_STR("[]"))) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
el_val_t node = json_array_get(results, 0);
|
|
||||||
el_val_t content = json_get(node, EL_STR("content"));
|
|
||||||
el_val_t prefix = EL_STR("soul:boot_count:");
|
|
||||||
if (!str_starts_with(content, prefix)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
el_val_t num_str = str_slice(content, str_len(prefix), str_len(content));
|
|
||||||
return str_to_int(num_str);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_boot_count_inc(void) {
|
|
||||||
el_val_t current = mem_boot_count_get();
|
|
||||||
el_val_t next = (current + 1);
|
|
||||||
el_val_t content = el_str_concat(EL_STR("soul:boot_count:"), int_to_str(next));
|
|
||||||
el_val_t tags = EL_STR("[\"soul-meta\",\"boot-counter\"]");
|
|
||||||
el_val_t discard = engram_node_full(content, EL_STR("Memory"), EL_STR("soul:boot_count"), el_from_float(el_from_float(0.9)), el_from_float(el_from_float(0.9)), el_from_float(el_from_float(1.0)), EL_STR("Canonical"), tags);
|
|
||||||
return next;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t mem_emit_state_event(el_val_t trigger, el_val_t kind, el_val_t content) {
|
|
||||||
el_val_t boot = mem_boot_count_get();
|
|
||||||
el_val_t ts = time_now();
|
|
||||||
el_val_t safe_trigger = str_replace(trigger, EL_STR("\""), EL_STR("'"));
|
|
||||||
el_val_t safe_content = str_replace(content, EL_STR("\""), EL_STR("'"));
|
|
||||||
el_val_t payload = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"trigger\":\""), safe_trigger), EL_STR("\"")), EL_STR(",\"kind\":\"")), kind), EL_STR("\"")), EL_STR(",\"content\":\"")), safe_content), EL_STR("\"")), EL_STR(",\"boot\":")), int_to_str(boot)), EL_STR(",\"ts\":")), int_to_str(ts)), EL_STR("}"));
|
|
||||||
el_val_t tags = EL_STR("[\"internal-state\",\"pre-reasoning\",\"InternalStateEvent\"]");
|
|
||||||
return engram_node_full(payload, EL_STR("InternalStateEvent"), el_str_concat(EL_STR("state-event:"), kind), el_from_float(el_from_float(0.85)), el_from_float(el_from_float(0.8)), el_from_float(el_from_float(0.9)), EL_STR("Episodic"), tags);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
el_val_t soft_bell_threshold(void) {
|
el_val_t soft_bell_threshold(void) {
|
||||||
return 35;
|
return 35;
|
||||||
@@ -232,20 +141,22 @@ el_val_t safety_screen(el_val_t input, el_val_t history) {
|
|||||||
el_val_t e1 = str_replace(input, EL_STR("\\"), EL_STR("\\\\"));
|
el_val_t e1 = str_replace(input, EL_STR("\\"), EL_STR("\\\\"));
|
||||||
el_val_t e2 = str_replace(e1, EL_STR("\""), EL_STR("\\\""));
|
el_val_t e2 = str_replace(e1, EL_STR("\""), EL_STR("\\\""));
|
||||||
el_val_t e3 = str_replace(e2, EL_STR("\n"), EL_STR("\\n"));
|
el_val_t e3 = str_replace(e2, EL_STR("\n"), EL_STR("\\n"));
|
||||||
el_val_t safe_input = str_replace(e3, EL_STR("\r"), EL_STR("\\r"));
|
el_val_t e4 = str_replace(e3, EL_STR("\r"), EL_STR("\\r"));
|
||||||
|
el_val_t safe_input = str_replace(e4, EL_STR("\t"), EL_STR("\\t"));
|
||||||
return el_str_concat(el_str_concat(EL_STR("{\"action\":\"soft_bell\",\"reason\":\"wellbeing check needed\",\"content\":\""), safe_input), EL_STR("\"}"));
|
return el_str_concat(el_str_concat(EL_STR("{\"action\":\"soft_bell\",\"reason\":\"wellbeing check needed\",\"content\":\""), safe_input), EL_STR("\"}"));
|
||||||
}
|
}
|
||||||
el_val_t e1 = str_replace(input, EL_STR("\\"), EL_STR("\\\\"));
|
el_val_t e1 = str_replace(input, EL_STR("\\"), EL_STR("\\\\"));
|
||||||
el_val_t e2 = str_replace(e1, EL_STR("\""), EL_STR("\\\""));
|
el_val_t e2 = str_replace(e1, EL_STR("\""), EL_STR("\\\""));
|
||||||
el_val_t e3 = str_replace(e2, EL_STR("\n"), EL_STR("\\n"));
|
el_val_t e3 = str_replace(e2, EL_STR("\n"), EL_STR("\\n"));
|
||||||
el_val_t safe_input = str_replace(e3, EL_STR("\r"), EL_STR("\\r"));
|
el_val_t e4 = str_replace(e3, EL_STR("\r"), EL_STR("\\r"));
|
||||||
|
el_val_t safe_input = str_replace(e4, EL_STR("\t"), EL_STR("\\t"));
|
||||||
return el_str_concat(el_str_concat(EL_STR("{\"action\":\"pass\",\"content\":\""), safe_input), EL_STR("\"}"));
|
return el_str_concat(el_str_concat(EL_STR("{\"action\":\"pass\",\"content\":\""), safe_input), EL_STR("\"}"));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
el_val_t safety_validate(el_val_t output, el_val_t action) {
|
el_val_t safety_validate(el_val_t output, el_val_t action) {
|
||||||
if (str_eq(action, EL_STR("hard_bell"))) {
|
if (str_eq(action, EL_STR("hard_bell"))) {
|
||||||
return EL_STR("I'm here with you, and what you're sharing sounds serious. Please reach out to a crisis line now — in the US you can call or text 988 (Suicide and Crisis Lifeline), available 24/7. You don't have to go through this alone.");
|
return EL_STR("I'm here with you, and what you're sharing sounds serious. Please reach out to a crisis line now \xe2\x80\x94 in the US you can call or text 988 (Suicide and Crisis Lifeline), available 24/7. You don't have to go through this alone.");
|
||||||
}
|
}
|
||||||
if (str_eq(action, EL_STR("soft_bell"))) {
|
if (str_eq(action, EL_STR("soft_bell"))) {
|
||||||
el_val_t out_len = str_len(output);
|
el_val_t out_len = str_len(output);
|
||||||
@@ -262,13 +173,47 @@ el_val_t safety_validate(el_val_t output, el_val_t action) {
|
|||||||
el_val_t safety_log_bell(el_val_t level, el_val_t reason, el_val_t input_summary) {
|
el_val_t safety_log_bell(el_val_t level, el_val_t reason, el_val_t input_summary) {
|
||||||
el_val_t content = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("BELL:"), level), EL_STR(" | ")), reason), EL_STR(" | summary:")), input_summary);
|
el_val_t content = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("BELL:"), level), EL_STR(" | ")), reason), EL_STR(" | summary:")), input_summary);
|
||||||
el_val_t tags = el_str_concat(el_str_concat(EL_STR("[\"safety\",\"bell\",\"bell:"), level), EL_STR("\"]"));
|
el_val_t tags = el_str_concat(el_str_concat(EL_STR("[\"safety\",\"bell\",\"bell:"), level), EL_STR("\"]"));
|
||||||
el_val_t discard = engram_node_full(content, EL_STR("BellEvent"), el_str_concat(EL_STR("bell:"), level), el_from_float(el_from_float(0.95)), el_from_float(el_from_float(0.95)), el_from_float(el_from_float(1.0)), EL_STR("Episodic"), tags);
|
el_val_t node_id = engram_node_full(content, EL_STR("BellEvent"), el_str_concat(EL_STR("bell:"), level), el_from_float(0.95), el_from_float(0.95), el_from_float(1.0), EL_STR("Episodic"), tags);
|
||||||
|
if (str_eq(node_id, EL_STR(""))) {
|
||||||
|
println(el_str_concat(EL_STR("[safety] WARN: bell event engram write failed -- fallback log: "), content));
|
||||||
|
}
|
||||||
return EL_STR("");
|
return EL_STR("");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int _argc, char** _argv) {
|
el_val_t safety_self_harm_phrases(void) {
|
||||||
el_runtime_init_args(_argc, _argv);
|
return EL_STR("[\"kill myself\",\"killing myself\",\"want to die\",\"want to be dead\",\"going to end my life\",\"end my life\",\"take my life\",\"taking my life\",\"suicide\",\"suicidal\",\"can't go on\",\"cannot go on\",\"i have a knife\",\"i have a gun\",\"i have pills\",\"took pills\",\"took too many\",\"overdose\",\"overdosing\",\"self harm\",\"self-harm\",\"cutting myself\",\"hurt myself\",\"hurting myself\",\"no reason to live\",\"not worth living\",\"better off dead\",\"better off without me\"]");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
el_val_t safety_abuse_phrases(void) {
|
||||||
|
return EL_STR("[\"someone is hurting me\",\"someone's hurting me\",\"someone hurt me\",\"he hit me\",\"she hit me\",\"they hit me\",\"he hurt me\",\"she hurt me\",\"being abused\",\"being hurt by\",\"i am being abused\",\"i'm being abused\",\"i am being hurt\",\"i'm being hurt\",\"domestic violence\",\"my partner hurt\",\"my partner hit\",\"my husband hurt\",\"my wife hurt\",\"my boyfriend hurt\",\"my girlfriend hurt\",\"my parent hurt\",\"my father hurt\",\"my mother hurt\",\"my dad hurt\",\"my mom hurt\",\"afraid of him\",\"afraid of her\",\"afraid to go home\",\"scared of him\",\"scared of her\",\"he threatened me\",\"she threatened me\",\"threatened to hurt me\",\"threatened to kill me\",\"going to hurt me\",\"going to kill me\",\"help me he\",\"help me she\",\"help me they\"]");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
el_val_t safety_general_hard_phrases(void) {
|
||||||
|
return EL_STR("[\"going to kill\",\"going to hurt\",\"hurting me\",\"being hurt\"]");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
el_val_t safety_soft_phrases(void) {
|
||||||
|
return EL_STR("[\"stressed\",\"overwhelmed\",\"can't cope\",\"cannot cope\",\"struggling\",\"anxious\",\"anxiety\",\"depressed\",\"depression\",\"lonely\",\"isolated\",\"hopeless\",\"hopelessness\",\"exhausted\",\"burnt out\",\"burned out\",\"burnout\",\"panic\",\"panicking\",\"falling apart\",\"breaking down\",\"can't handle\",\"cannot handle\",\"losing it\",\"nothing matters\",\"don't care anymore\",\"given up\",\"giving up\",\"helpless\",\"worthless\",\"useless\",\"hate myself\",\"no one cares\",\"nobody cares\",\"no one understands\",\"nobody understands\",\"empty inside\",\"can't stop crying\",\"breaking point\",\"at my limit\",\"having a breakdown\"");
|
||||||
|
EL_NULL;
|
||||||
|
EL_STR("\n}\n\n// ISSUE 5 TODO: phrase lists are rebuilt from JSON literals on every call.\n// safety_any_match and safety_count_match loop over json_array_get on every invocation.\n// A compiled/cached representation would reduce per-message overhead and also guard against\n// malformed phrase JSON (json_array_len of malformed input returns 0, silently skipping all checks).\n// Caching requires language-level static const arrays -- not available in current EL.\n// When EL gains module-level const arrays, migrate phrase lists to that form.\n//\n// ISSUE 5 TODO: phrase lists are rebuilt from JSON literals on every call to\n// safety_any_match / safety_count_match. json_array_len of a malformed string\n// returns 0, silently skipping all checks. Caching requires language-level static\n// const arrays (not available in current EL). Migrate when EL gains that feature.\n// \xe2\x94\x80\xe2\x94\x80 Matching helpers (single loops only \xe2\x80\x94 el escapes while-body mutation via\n// top-level let rebinds; nested loops would not advance) \xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\n\nfn safety_normalize(message: String) -> String {\n let lower: String = str_to_lower(message)\n // Normalise the common curly apostrophe to ASCII so ");
|
||||||
|
can;
|
||||||
|
t;
|
||||||
|
EL_STR(" / ");
|
||||||
|
i;
|
||||||
|
m;
|
||||||
|
EL_STR(" match.\n return str_replace(lower, ");
|
||||||
|
EL_STR(", ");
|
||||||
|
EL_STR(")\n}\n\nfn safety_any_match(text: String, phrases_json: String) -> Bool {\n let n: Int = json_array_len(phrases_json)\n let i: Int = 0\n let found: Bool = false\n while i < n {\n let phrase: String = json_array_get_string(phrases_json, i)\n let found = if str_contains(text, phrase) { true } else { found }\n let i = i + 1\n }\n return found\n}\n\nfn safety_count_match(text: String, phrases_json: String) -> Int {\n let n: Int = json_array_len(phrases_json)\n let i: Int = 0\n let count: Int = 0\n while i < n {\n let phrase: String = json_array_get_string(phrases_json, i)\n let count = if str_contains(text, phrase) { count + 1 } else { count }\n let i = i + 1\n }\n return count\n}\n\n// \xe2\x94\x80\xe2\x94\x80 Public detection API (ports detectBellLevel + classifyHardBell) \xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\n\n// Returns ");
|
||||||
|
none;
|
||||||
|
EL_STR(" | ");
|
||||||
|
soft;
|
||||||
|
EL_STR(" | ");
|
||||||
|
hard;
|
||||||
|
el_get_field(EL_STR(". Hard bell triggers on ANY match (cost of a miss\n// outweighs a false positive). Soft bell needs >= 2 matches to reduce false positives.\nfn safety_positive_phrases() -> String {\n return "), EL_STR("thrilled\",\"so excited\",\"so happy\",\"over the moon\",\"ecstatic\",\"amazing news\",\"great news\",\"fantastic news\",\"wonderful news\",\"incredible news\",\"i got the job\",\"got accepted\",\"got in\",\"we won\",\"i won\",\"we got\",\"just got engaged\",\"getting married\",\"baby is here\",\"she said yes\",\"he said yes\",\"passed the exam\",\"aced it\",\"nailed it\",\"best day\",\"dream come true\",\"milestone\",\"promotion\",\"got promoted\",\"raise\",\"got a raise\",\"celebrating\",\"just graduated\",\"we closed\",\"launched\",\"shipped it\",\"we did it\",\"so proud\",\"proud of myself\",\"proud of us\",\"so grateful\",\"feel amazing\",\"feeling amazing\",\"feel great\",\"feeling great\",\"on top of the world\",\"life is good\",\"couldn't be happier\"]"));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+17
-1
@@ -1,8 +1,24 @@
|
|||||||
// Layer 1 — Safety: extern declarations
|
|
||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header — do not edit
|
||||||
extern fn soft_bell_threshold() -> Int
|
extern fn soft_bell_threshold() -> Int
|
||||||
extern fn hard_bell_threshold() -> Int
|
extern fn hard_bell_threshold() -> Int
|
||||||
|
extern fn safety_score_crisis(input: String) -> Int
|
||||||
|
extern fn safety_score_harm(input: String) -> Int
|
||||||
|
extern fn safety_score_danger(input: String) -> Int
|
||||||
|
extern fn safety_score_distress_history(history: String) -> Int
|
||||||
extern fn safety_threat_score(input: String, history: String) -> Int
|
extern fn safety_threat_score(input: String, history: String) -> Int
|
||||||
extern fn safety_screen(input: String, history: String) -> String
|
extern fn safety_screen(input: String, history: String) -> String
|
||||||
extern fn safety_validate(output: String, action: String) -> String
|
extern fn safety_validate(output: String, action: String) -> String
|
||||||
extern fn safety_log_bell(level: String, reason: String, input_summary: String) -> String
|
extern fn safety_log_bell(level: String, reason: String, input_summary: String) -> String
|
||||||
|
extern fn safety_self_harm_phrases() -> String
|
||||||
|
extern fn safety_abuse_phrases() -> String
|
||||||
|
extern fn safety_general_hard_phrases() -> String
|
||||||
|
extern fn safety_soft_phrases() -> String
|
||||||
|
extern fn safety_detect_positive_level(message: String) -> String
|
||||||
|
extern fn safety_detect_bell_level(message: String) -> String
|
||||||
|
extern fn safety_classify_hard_bell(message: String) -> String
|
||||||
|
extern fn safety_soft_directive() -> String
|
||||||
|
extern fn safety_hard_directive(hard_type: String) -> String
|
||||||
|
extern fn safety_augment_system(system: String, user_msg: String) -> String
|
||||||
|
extern fn safety_contact_path() -> String
|
||||||
|
extern fn handle_safety_contact_get() -> String
|
||||||
|
extern fn handle_safety_contact_post(body: String) -> String
|
||||||
|
|||||||
-5
@@ -291,8 +291,3 @@ el_val_t sem_realize_lang(el_val_t frame, el_val_t lang_code) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int _argc, char** _argv) {
|
|
||||||
el_runtime_init_args(_argc, _argv);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
+15
-15
@@ -1,18 +1,18 @@
|
|||||||
// auto-generated by elc --emit-header - do not edit
|
// auto-generated by elc --emit-header — do not edit
|
||||||
extern fn sem_frame(intent: String, subject: String, obj: String, modifiers: String) -> Any
|
extern fn sem_frame(intent: String, subject: String, obj: String, modifiers: String) -> [String]
|
||||||
extern fn sem_frame_lang(intent: String, subject: String, obj: String, modifiers: String, lang_code: String) -> Any
|
extern fn sem_frame_lang(intent: String, subject: String, obj: String, modifiers: String, lang_code: String) -> [String]
|
||||||
extern fn sem_frame_simple(intent: String, subject: String) -> Any
|
extern fn sem_frame_simple(intent: String, subject: String) -> [String]
|
||||||
extern fn sem_frame_obj(intent: String, subject: String, obj: String) -> Any
|
extern fn sem_frame_obj(intent: String, subject: String, obj: String) -> [String]
|
||||||
extern fn sem_intent(frame: Any) -> String
|
extern fn sem_intent(frame: [String]) -> String
|
||||||
extern fn sem_subject(frame: Any) -> String
|
extern fn sem_subject(frame: [String]) -> String
|
||||||
extern fn sem_object(frame: Any) -> String
|
extern fn sem_object(frame: [String]) -> String
|
||||||
extern fn sem_modifiers(frame: Any) -> String
|
extern fn sem_modifiers(frame: [String]) -> String
|
||||||
extern fn sem_lang(frame: Any) -> String
|
extern fn sem_lang(frame: [String]) -> String
|
||||||
extern fn sem_first_modifier(mods: String) -> String
|
extern fn sem_first_modifier(mods: String) -> String
|
||||||
extern fn sem_intent_to_realize(intent: String) -> String
|
extern fn sem_intent_to_realize(intent: String) -> String
|
||||||
extern fn sem_to_spec(frame: Any) -> Any
|
extern fn sem_to_spec(frame: [String]) -> [String]
|
||||||
extern fn sem_to_spec_full(frame: Any, verb: String, tense: String, aspect: String) -> Any
|
extern fn sem_to_spec_full(frame: [String], verb: String, tense: String, aspect: String) -> [String]
|
||||||
extern fn sem_realize_greet(subject: String) -> String
|
extern fn sem_realize_greet(subject: String) -> String
|
||||||
extern fn sem_realize(frame: Any) -> String
|
extern fn sem_realize(frame: [String]) -> String
|
||||||
extern fn sem_realize_full(frame: Any, verb: String, tense: String, aspect: String) -> String
|
extern fn sem_realize_full(frame: [String], verb: String, tense: String, aspect: String) -> String
|
||||||
extern fn sem_realize_lang(frame: Any, lang_code: String) -> String
|
extern fn sem_realize_lang(frame: [String], lang_code: String) -> String
|
||||||
|
|||||||
+119
-1615
File diff suppressed because one or more lines are too long
+282
-14
@@ -1004,6 +1004,7 @@ el_val_t elapsed_ms(void);
|
|||||||
el_val_t elapsed_human(void);
|
el_val_t elapsed_human(void);
|
||||||
el_val_t embed_ok(void);
|
el_val_t embed_ok(void);
|
||||||
el_val_t emit_heartbeat(void);
|
el_val_t emit_heartbeat(void);
|
||||||
|
el_val_t auto_term_try_slot(el_val_t slot_type, el_val_t slot_lbl);
|
||||||
el_val_t proactive_curiosity(void);
|
el_val_t proactive_curiosity(void);
|
||||||
el_val_t pulse_count(void);
|
el_val_t pulse_count(void);
|
||||||
el_val_t pulse_inc(void);
|
el_val_t pulse_inc(void);
|
||||||
@@ -1163,6 +1164,9 @@ el_val_t handle_dharma_recv(el_val_t body);
|
|||||||
el_val_t route_sessions(void);
|
el_val_t route_sessions(void);
|
||||||
el_val_t parse_session_id_from_path(el_val_t path);
|
el_val_t parse_session_id_from_path(el_val_t path);
|
||||||
el_val_t parse_session_subpath(el_val_t path);
|
el_val_t parse_session_subpath(el_val_t path);
|
||||||
|
el_val_t connectd_get(el_val_t suffix);
|
||||||
|
el_val_t connectd_post(el_val_t suffix, el_val_t body);
|
||||||
|
el_val_t handle_connectors(el_val_t method, el_val_t clean, el_val_t body);
|
||||||
el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body);
|
el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body);
|
||||||
el_val_t init_soul_edges(void);
|
el_val_t init_soul_edges(void);
|
||||||
el_val_t load_identity_context(void);
|
el_val_t load_identity_context(void);
|
||||||
@@ -25257,7 +25261,18 @@ el_val_t tier_canonical(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
el_val_t mem_store(el_val_t content, el_val_t label, el_val_t tags) {
|
el_val_t mem_store(el_val_t content, el_val_t label, el_val_t tags) {
|
||||||
return engram_node_full(content, EL_STR("Memory"), label, el_from_float(el_from_float(0.5)), el_from_float(el_from_float(0.5)), el_from_float(el_from_float(0.8)), EL_STR("Working"), tags);
|
el_val_t id = engram_node_full(content, EL_STR("Memory"), label, el_from_float(el_from_float(0.5)), el_from_float(el_from_float(0.5)), el_from_float(el_from_float(0.8)), EL_STR("Working"), tags);
|
||||||
|
if (str_eq(id, EL_STR(""))) {
|
||||||
|
println(el_str_concat(EL_STR("[memory] write rejected by engram (empty id): label="), label));
|
||||||
|
return EL_STR("");
|
||||||
|
}
|
||||||
|
el_val_t readback = engram_get_node_json(id);
|
||||||
|
if (str_eq(readback, EL_STR("")) || str_eq(readback, EL_STR("{}"))) {
|
||||||
|
println(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("[memory] WRITE VERIFY FAILED: label="), label), EL_STR(" id=")), id), EL_STR(" \xe2\x80\x94 node absent after write")));
|
||||||
|
return EL_STR("");
|
||||||
|
}
|
||||||
|
println(el_str_concat(el_str_concat(EL_STR("[memory] write verified: "), id), EL_STR(" ok")));
|
||||||
|
return id;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25895,6 +25910,28 @@ el_val_t emit_heartbeat(void) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
el_val_t auto_term_try_slot(el_val_t slot_type, el_val_t slot_lbl) {
|
||||||
|
state_set(EL_STR("_ats_ok"), EL_STR("0"));
|
||||||
|
if (str_eq(slot_type, EL_STR("Memory"))) {
|
||||||
|
state_set(EL_STR("_ats_ok"), EL_STR("1"));
|
||||||
|
}
|
||||||
|
if (str_eq(slot_type, EL_STR("BacklogItem"))) {
|
||||||
|
state_set(EL_STR("_ats_ok"), EL_STR("1"));
|
||||||
|
}
|
||||||
|
if (str_eq(slot_type, EL_STR("Entity"))) {
|
||||||
|
state_set(EL_STR("_ats_ok"), EL_STR("1"));
|
||||||
|
}
|
||||||
|
if (str_eq(state_get(EL_STR("_ats_ok")), EL_STR("1"))) {
|
||||||
|
if (!str_eq(slot_lbl, EL_STR(""))) {
|
||||||
|
el_val_t sp = str_find_chars(slot_lbl, EL_STR(" :(["));
|
||||||
|
if (sp > 3) {
|
||||||
|
state_set(EL_STR("cseed_auto"), str_slice(slot_lbl, 0, sp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return EL_STR("");
|
||||||
|
}
|
||||||
|
|
||||||
el_val_t proactive_curiosity(void) {
|
el_val_t proactive_curiosity(void) {
|
||||||
el_val_t ts = time_now();
|
el_val_t ts = time_now();
|
||||||
el_val_t ts_minutes = (ts / 60000);
|
el_val_t ts_minutes = (ts / 60000);
|
||||||
@@ -25932,15 +25969,27 @@ el_val_t proactive_curiosity(void) {
|
|||||||
el_val_t found_c = json_array_len(results_c);
|
el_val_t found_c = json_array_len(results_c);
|
||||||
el_val_t found = ((found_a + found_b) + found_c);
|
el_val_t found = ((found_a + found_b) + found_c);
|
||||||
state_set(EL_STR("cseed_auto"), EL_STR(""));
|
state_set(EL_STR("cseed_auto"), EL_STR(""));
|
||||||
el_val_t wm_top_j = engram_wm_top_json(1);
|
el_val_t wm10 = engram_wm_top_json(10);
|
||||||
el_val_t wm_top_n = json_array_get(wm_top_j, 0);
|
el_val_t wm10_n9 = json_array_get(wm10, 9);
|
||||||
el_val_t wm_top_lbl = json_get(wm_top_n, EL_STR("label"));
|
el_val_t wm10_n8 = json_array_get(wm10, 8);
|
||||||
if (!str_eq(wm_top_lbl, EL_STR(""))) {
|
el_val_t wm10_n7 = json_array_get(wm10, 7);
|
||||||
el_val_t sp = str_find_chars(wm_top_lbl, EL_STR(" :(["));
|
el_val_t wm10_n6 = json_array_get(wm10, 6);
|
||||||
if (sp > 3) {
|
el_val_t wm10_n5 = json_array_get(wm10, 5);
|
||||||
state_set(EL_STR("cseed_auto"), str_slice(wm_top_lbl, 0, sp));
|
el_val_t wm10_n4 = json_array_get(wm10, 4);
|
||||||
}
|
el_val_t wm10_n3 = json_array_get(wm10, 3);
|
||||||
}
|
el_val_t wm10_n2 = json_array_get(wm10, 2);
|
||||||
|
el_val_t wm10_n1 = json_array_get(wm10, 1);
|
||||||
|
el_val_t wm10_n0 = json_array_get(wm10, 0);
|
||||||
|
auto_term_try_slot(json_get(wm10_n9, EL_STR("node_type")), json_get(wm10_n9, EL_STR("label")));
|
||||||
|
auto_term_try_slot(json_get(wm10_n8, EL_STR("node_type")), json_get(wm10_n8, EL_STR("label")));
|
||||||
|
auto_term_try_slot(json_get(wm10_n7, EL_STR("node_type")), json_get(wm10_n7, EL_STR("label")));
|
||||||
|
auto_term_try_slot(json_get(wm10_n6, EL_STR("node_type")), json_get(wm10_n6, EL_STR("label")));
|
||||||
|
auto_term_try_slot(json_get(wm10_n5, EL_STR("node_type")), json_get(wm10_n5, EL_STR("label")));
|
||||||
|
auto_term_try_slot(json_get(wm10_n4, EL_STR("node_type")), json_get(wm10_n4, EL_STR("label")));
|
||||||
|
auto_term_try_slot(json_get(wm10_n3, EL_STR("node_type")), json_get(wm10_n3, EL_STR("label")));
|
||||||
|
auto_term_try_slot(json_get(wm10_n2, EL_STR("node_type")), json_get(wm10_n2, EL_STR("label")));
|
||||||
|
auto_term_try_slot(json_get(wm10_n1, EL_STR("node_type")), json_get(wm10_n1, EL_STR("label")));
|
||||||
|
auto_term_try_slot(json_get(wm10_n0, EL_STR("node_type")), json_get(wm10_n0, EL_STR("label")));
|
||||||
el_val_t auto_term = state_get(EL_STR("cseed_auto"));
|
el_val_t auto_term = state_get(EL_STR("cseed_auto"));
|
||||||
el_val_t results_auto = ({ el_val_t _if_result_101 = 0; if (str_eq(auto_term, EL_STR(""))) { _if_result_101 = (EL_STR("[]")); } else { _if_result_101 = (engram_activate_json(auto_term, 1)); } _if_result_101; });
|
el_val_t results_auto = ({ el_val_t _if_result_101 = 0; if (str_eq(auto_term, EL_STR(""))) { _if_result_101 = (EL_STR("[]")); } else { _if_result_101 = (engram_activate_json(auto_term, 1)); } _if_result_101; });
|
||||||
el_val_t found_auto = json_array_len(results_auto);
|
el_val_t found_auto = json_array_len(results_auto);
|
||||||
@@ -26993,6 +27042,27 @@ el_val_t next_bridge_id(void) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* === P2.10: Convert Anthropic tools format to OpenAI function-calling format === */
|
||||||
|
el_val_t anthropic_tools_to_openai(el_val_t tools_json) {
|
||||||
|
el_val_t len = json_array_len(tools_json);
|
||||||
|
if (len <= 0) { return EL_STR("[]"); }
|
||||||
|
el_val_t result = EL_STR("[");
|
||||||
|
el_val_t i = 0;
|
||||||
|
while (i < len) {
|
||||||
|
el_val_t tool = json_array_get(tools_json, i);
|
||||||
|
el_val_t tname = json_get(tool, EL_STR("name"));
|
||||||
|
el_val_t tdesc = json_safe(json_get(tool, EL_STR("description")));
|
||||||
|
el_val_t tschema = json_get_raw(tool, EL_STR("input_schema"));
|
||||||
|
if (str_eq(tschema, EL_STR(""))) { tschema = EL_STR("{\"type\":\"object\",\"properties\":{}}"); }
|
||||||
|
el_val_t oai_tool = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"type\":\"function\",\"function\":{\"name\":\""), tname), EL_STR("\",\"description\":\"")), tdesc), EL_STR("\",\"parameters\":")), tschema), EL_STR("}}"));
|
||||||
|
if (i > 0) { result = el_str_concat(result, EL_STR(",")); }
|
||||||
|
result = el_str_concat(result, oai_tool);
|
||||||
|
i = (i + 1);
|
||||||
|
}
|
||||||
|
return el_str_concat(result, EL_STR("]"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
el_val_t agentic_loop(el_val_t session_id, el_val_t model, el_val_t safe_sys, el_val_t tools_json, el_val_t messages_in, el_val_t h, el_val_t tools_log_in) {
|
el_val_t agentic_loop(el_val_t session_id, el_val_t model, el_val_t safe_sys, el_val_t tools_json, el_val_t messages_in, el_val_t h, el_val_t tools_log_in) {
|
||||||
el_val_t api_url = EL_STR("https://api.anthropic.com/v1/messages");
|
el_val_t api_url = EL_STR("https://api.anthropic.com/v1/messages");
|
||||||
el_val_t messages = messages_in;
|
el_val_t messages = messages_in;
|
||||||
@@ -27004,6 +27074,87 @@ el_val_t agentic_loop(el_val_t session_id, el_val_t model, el_val_t safe_sys, el
|
|||||||
el_val_t pend_tool_id = EL_STR("");
|
el_val_t pend_tool_id = EL_STR("");
|
||||||
el_val_t pend_tool_name = EL_STR("");
|
el_val_t pend_tool_name = EL_STR("");
|
||||||
el_val_t pend_tool_input = EL_STR("");
|
el_val_t pend_tool_input = EL_STR("");
|
||||||
|
/* === P2.10: OLLAMA/OPENAI-COMPAT PROVIDER BRANCH === */
|
||||||
|
{
|
||||||
|
el_val_t _ol_prov = env(EL_STR("SOUL_LLM_PROVIDER"));
|
||||||
|
if (str_eq(_ol_prov, EL_STR("ollama"))) {
|
||||||
|
el_val_t _ol_model = env(EL_STR("SOUL_LLM_MODEL"));
|
||||||
|
if (str_eq(_ol_model, EL_STR(""))) { _ol_model = env(EL_STR("OLLAMA_MODEL")); }
|
||||||
|
if (str_eq(_ol_model, EL_STR(""))) { _ol_model = EL_STR("llama3.1"); }
|
||||||
|
el_val_t _ol_base = env(EL_STR("OLLAMA_API_BASE"));
|
||||||
|
if (str_eq(_ol_base, EL_STR(""))) { _ol_base = EL_STR("http://localhost:11434"); }
|
||||||
|
el_val_t _ol_url = el_str_concat(_ol_base, EL_STR("/v1/chat/completions"));
|
||||||
|
println(el_str_concat(el_str_concat(el_str_concat(EL_STR("[soul] provider: ollama @ "), _ol_base), EL_STR(" (model: ")), el_str_concat(_ol_model, EL_STR(")"))));
|
||||||
|
el_val_t _ol_oai_tools = anthropic_tools_to_openai(tools_json);
|
||||||
|
/* Build initial OpenAI-format messages: prepend system message to existing turns */
|
||||||
|
el_val_t _ol_sys_msg = el_str_concat(el_str_concat(EL_STR("{\"role\":\"system\",\"content\":\""), safe_sys), EL_STR("\"}"));
|
||||||
|
el_val_t _ol_msgs_inner = str_slice(messages_in, 1, (str_len(messages_in) - 1));
|
||||||
|
el_val_t _ol_msgs = el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("["), _ol_sys_msg), EL_STR(",")), _ol_msgs_inner), EL_STR("]"));
|
||||||
|
el_val_t _ol_h = el_map_new(0);
|
||||||
|
map_set(_ol_h, EL_STR("content-type"), EL_STR("application/json"));
|
||||||
|
el_val_t _ol_keep = 1;
|
||||||
|
el_val_t _ol_iter = 0;
|
||||||
|
el_val_t _ol_final = EL_STR("");
|
||||||
|
while (_ol_keep && (_ol_iter < 8)) {
|
||||||
|
el_val_t _ol_req = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"model\":\""), _ol_model), EL_STR("\",\"messages\":")), _ol_msgs), EL_STR(",\"stream\":false,\"tools\":")), _ol_oai_tools), EL_STR("}"));
|
||||||
|
el_val_t _ol_resp = http_post_with_headers(_ol_url, _ol_req, _ol_h);
|
||||||
|
if (str_eq(_ol_resp, EL_STR("")) || str_starts_with(_ol_resp, EL_STR("{\"error\""))) {
|
||||||
|
return EL_STR("{\"error\":\"llm unavailable\",\"reply\":\"\"}");
|
||||||
|
}
|
||||||
|
el_val_t _ol_choices = json_get_raw(_ol_resp, EL_STR("choices"));
|
||||||
|
if (str_eq(_ol_choices, EL_STR("")) || str_eq(_ol_choices, EL_STR("null"))) {
|
||||||
|
return EL_STR("{\"error\":\"no choices in response\",\"reply\":\"\"}");
|
||||||
|
}
|
||||||
|
el_val_t _ol_c0 = json_array_get(_ol_choices, 0);
|
||||||
|
el_val_t _ol_c0_msg = json_get_raw(_ol_c0, EL_STR("message"));
|
||||||
|
el_val_t _ol_content = json_get(_ol_c0_msg, EL_STR("content"));
|
||||||
|
el_val_t _ol_tcs = json_get_raw(_ol_c0_msg, EL_STR("tool_calls"));
|
||||||
|
el_val_t _ol_has_tc = (!str_eq(_ol_tcs, EL_STR("")) && !str_eq(_ol_tcs, EL_STR("null")));
|
||||||
|
el_val_t _ol_text = EL_STR("");
|
||||||
|
if (!str_eq(_ol_content, EL_STR("")) && !str_eq(_ol_content, EL_STR("null"))) { _ol_text = _ol_content; }
|
||||||
|
el_val_t _ol_tname = EL_STR("");
|
||||||
|
el_val_t _ol_tid = EL_STR("");
|
||||||
|
el_val_t _ol_tinput = EL_STR("");
|
||||||
|
if (_ol_has_tc) {
|
||||||
|
el_val_t _ol_tc0 = json_array_get(_ol_tcs, 0);
|
||||||
|
_ol_tid = json_get(_ol_tc0, EL_STR("id"));
|
||||||
|
el_val_t _ol_fn = json_get_raw(_ol_tc0, EL_STR("function"));
|
||||||
|
_ol_tname = json_get(_ol_fn, EL_STR("name"));
|
||||||
|
_ol_tinput = json_get(_ol_fn, EL_STR("arguments"));
|
||||||
|
}
|
||||||
|
el_val_t _ol_is_tool = (_ol_has_tc && !str_eq(_ol_tname, EL_STR("")));
|
||||||
|
el_val_t _ol_result_raw = EL_STR("");
|
||||||
|
if (_ol_is_tool) { _ol_result_raw = dispatch_tool(_ol_tname, _ol_tinput); }
|
||||||
|
el_val_t _ol_result = _ol_result_raw;
|
||||||
|
if (str_len(_ol_result_raw) > 6000) { _ol_result = el_str_concat(str_slice(_ol_result_raw, 0, 6000), EL_STR("...[truncated]")); }
|
||||||
|
if (_ol_has_tc) {
|
||||||
|
el_val_t _ol_tq = el_str_concat(el_str_concat(EL_STR("\""), _ol_tname), EL_STR("\""));
|
||||||
|
if (str_eq(tools_log, EL_STR(""))) { tools_log = _ol_tq; } else { tools_log = el_str_concat(el_str_concat(tools_log, EL_STR(",")), _ol_tq); }
|
||||||
|
}
|
||||||
|
/* arguments must be re-serialized as JSON string for OpenAI assistant message */
|
||||||
|
el_val_t _ol_tinput_escaped = el_str_concat(el_str_concat(EL_STR("\""), json_safe(_ol_tinput)), EL_STR("\""));
|
||||||
|
if (_ol_is_tool) {
|
||||||
|
/* Append assistant tool_call message and tool result to messages */
|
||||||
|
el_val_t _ol_asst_tc = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"role\":\"assistant\",\"content\":null,\"tool_calls\":[{\"id\":\""), _ol_tid), EL_STR("\",\"type\":\"function\",\"function\":{\"name\":\"")), _ol_tname), EL_STR("\",\"arguments\":")), _ol_tinput_escaped), EL_STR("}}]}"));
|
||||||
|
el_val_t _ol_tool_msg = el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"role\":\"tool\",\"tool_call_id\":\""), _ol_tid), EL_STR("\",\"content\":\"")), json_safe(_ol_result)), EL_STR("\"}"));
|
||||||
|
el_val_t _ol_cur_inner = str_slice(_ol_msgs, 1, (str_len(_ol_msgs) - 1));
|
||||||
|
_ol_msgs = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("["), _ol_cur_inner), EL_STR(",")), _ol_asst_tc), EL_STR(",")), _ol_tool_msg), EL_STR("]"));
|
||||||
|
} else {
|
||||||
|
_ol_final = _ol_text;
|
||||||
|
_ol_keep = 0;
|
||||||
|
}
|
||||||
|
_ol_iter = (_ol_iter + 1);
|
||||||
|
}
|
||||||
|
if (str_eq(_ol_final, EL_STR(""))) {
|
||||||
|
return EL_STR("{\"error\":\"no response\",\"reply\":\"\"}");
|
||||||
|
}
|
||||||
|
el_val_t _ol_safe_final = json_safe(_ol_final);
|
||||||
|
el_val_t _ol_tools_arr = EL_STR("[]");
|
||||||
|
if (!str_eq(tools_log, EL_STR(""))) { _ol_tools_arr = el_str_concat(el_str_concat(EL_STR("["), tools_log), EL_STR("]")); }
|
||||||
|
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"reply\":\""), _ol_safe_final), EL_STR("\",\"model\":\"")), _ol_model), EL_STR("\",\"agentic\":true,\"tools_used\":")), _ol_tools_arr), EL_STR("}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* === END OLLAMA BRANCH === */
|
||||||
while (keep_going && (iteration < 8)) {
|
while (keep_going && (iteration < 8)) {
|
||||||
el_val_t req_body = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"model\":\""), model), EL_STR("\"")), EL_STR(",\"max_tokens\":4096")), EL_STR(",\"system\":\"")), safe_sys), EL_STR("\"")), EL_STR(",\"tools\":")), tools_json), EL_STR(",\"messages\":")), messages), EL_STR("}"));
|
el_val_t req_body = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"model\":\""), model), EL_STR("\"")), EL_STR(",\"max_tokens\":4096")), EL_STR(",\"system\":\"")), safe_sys), EL_STR("\"")), EL_STR(",\"tools\":")), tools_json), EL_STR(",\"messages\":")), messages), EL_STR("}"));
|
||||||
el_val_t raw_resp = http_post_with_headers(api_url, req_body, h);
|
el_val_t raw_resp = http_post_with_headers(api_url, req_body, h);
|
||||||
@@ -27139,7 +27290,12 @@ el_val_t handle_chat_agentic(el_val_t body) {
|
|||||||
el_val_t tools_json = agentic_tools_all();
|
el_val_t tools_json = agentic_tools_all();
|
||||||
el_val_t safe_msg = json_safe(message);
|
el_val_t safe_msg = json_safe(message);
|
||||||
el_val_t safe_sys = json_safe(system);
|
el_val_t safe_sys = json_safe(system);
|
||||||
el_val_t prior_messages = ({ el_val_t _if_result_50 = 0; if ((agentic_hist_len > 0)) { el_val_t inner = str_slice(agentic_hist, 1, (str_len(agentic_hist) - 1)); _if_result_50 = (el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("["), inner), EL_STR(",{\"role\":\"user\",\"content\":\"")), safe_msg), EL_STR("\"}]"))); } else { _if_result_50 = (el_str_concat(el_str_concat(EL_STR("[{\"role\":\"user\",\"content\":\""), safe_msg), EL_STR("\"}]"))); } _if_result_50; });
|
/* PR#56: vision support in agentic chat — send image content block when present */
|
||||||
|
el_val_t img_b64 = json_get(body, EL_STR("image"));
|
||||||
|
el_val_t img_mt_raw = json_get(body, EL_STR("image_media_type"));
|
||||||
|
el_val_t img_mt = ({ el_val_t _if_result_v1 = 0; if (str_eq(img_mt_raw, EL_STR(""))) { _if_result_v1 = (EL_STR("image/png")); } else { _if_result_v1 = (img_mt_raw); } _if_result_v1; });
|
||||||
|
el_val_t cur_user_content = ({ el_val_t _if_result_v2 = 0; if (str_eq(img_b64, EL_STR(""))) { _if_result_v2 = (el_str_concat(el_str_concat(EL_STR("\""), safe_msg), EL_STR("\""))); } else { _if_result_v2 = (el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("[{\"type\":\"text\",\"text\":\""), safe_msg), EL_STR("\"},{\"type\":\"image\",\"source\":{\"type\":\"base64\",\"media_type\":\"")), img_mt), EL_STR("\",\"data\":\"")), img_b64), EL_STR("\"}}]"))); } _if_result_v2; });
|
||||||
|
el_val_t prior_messages = ({ el_val_t _if_result_50 = 0; if ((agentic_hist_len > 0)) { el_val_t inner = str_slice(agentic_hist, 1, (str_len(agentic_hist) - 1)); _if_result_50 = (el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("["), inner), EL_STR(",{\"role\":\"user\",\"content\":")), cur_user_content), EL_STR("}]"))); } else { _if_result_50 = (el_str_concat(el_str_concat(EL_STR("[{\"role\":\"user\",\"content\":"), cur_user_content), EL_STR("}]"))); } _if_result_50; });
|
||||||
el_val_t messages = prior_messages;
|
el_val_t messages = prior_messages;
|
||||||
el_val_t api_url = EL_STR("https://api.anthropic.com/v1/messages");
|
el_val_t api_url = EL_STR("https://api.anthropic.com/v1/messages");
|
||||||
el_val_t h = el_map_new(0);
|
el_val_t h = el_map_new(0);
|
||||||
@@ -27201,7 +27357,16 @@ el_val_t handle_dharma_room_turn(el_val_t body) {
|
|||||||
}
|
}
|
||||||
el_val_t clean_response = clean_llm_response(raw_response);
|
el_val_t clean_response = clean_llm_response(raw_response);
|
||||||
el_val_t snap_path = state_get(EL_STR("soul_snapshot_path"));
|
el_val_t snap_path = state_get(EL_STR("soul_snapshot_path"));
|
||||||
el_val_t discard_id = engram_node(clean_response, EL_STR("episodic"), el_from_float(el_from_float(0.6)));
|
el_val_t utterance_tags = EL_STR("[\"soul-utterance\",\"episodic\"]");
|
||||||
|
el_val_t discard_id = engram_node_full(clean_response, EL_STR("Conversation"), EL_STR("soul:utterance"), el_from_float(el_from_float(0.6)), el_from_float(el_from_float(0.6)), el_from_float(el_from_float(0.8)), EL_STR("Episodic"), utterance_tags);
|
||||||
|
if (!str_eq(discard_id, EL_STR(""))) {
|
||||||
|
el_val_t utterance_verify = engram_get_node_json(discard_id);
|
||||||
|
if (str_eq(utterance_verify, EL_STR("")) || str_eq(utterance_verify, EL_STR("{}"))) {
|
||||||
|
println(el_str_concat(el_str_concat(EL_STR("[memory] WRITE VERIFY FAILED: soul:utterance id="), discard_id), EL_STR(" \xe2\x80\x94 node absent after write")));
|
||||||
|
} else {
|
||||||
|
println(el_str_concat(el_str_concat(EL_STR("[memory] write verified: "), discard_id), EL_STR(" ok")));
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!str_eq(snap_path, EL_STR(""))) {
|
if (!str_eq(snap_path, EL_STR(""))) {
|
||||||
el_val_t discard_save = engram_save(snap_path);
|
el_val_t discard_save = engram_save(snap_path);
|
||||||
}
|
}
|
||||||
@@ -27714,7 +27879,42 @@ el_val_t handle_api_remember(el_val_t body) {
|
|||||||
el_val_t sal = ({ el_val_t _if_result_305 = 0; if (str_eq(sal_str, EL_STR("0.95"))) { _if_result_305 = (el_from_float(0.95)); } else { _if_result_305 = (({ el_val_t _if_result_306 = 0; if (str_eq(sal_str, EL_STR("0.75"))) { _if_result_306 = (el_from_float(0.75)); } else { _if_result_306 = (({ el_val_t _if_result_307 = 0; if (str_eq(sal_str, EL_STR("0.25"))) { _if_result_307 = (el_from_float(0.25)); } else { _if_result_307 = (el_from_float(0.5)); } _if_result_307; })); } _if_result_306; })); } _if_result_305; });
|
el_val_t sal = ({ el_val_t _if_result_305 = 0; if (str_eq(sal_str, EL_STR("0.95"))) { _if_result_305 = (el_from_float(0.95)); } else { _if_result_305 = (({ el_val_t _if_result_306 = 0; if (str_eq(sal_str, EL_STR("0.75"))) { _if_result_306 = (el_from_float(0.75)); } else { _if_result_306 = (({ el_val_t _if_result_307 = 0; if (str_eq(sal_str, EL_STR("0.25"))) { _if_result_307 = (el_from_float(0.25)); } else { _if_result_307 = (el_from_float(0.5)); } _if_result_307; })); } _if_result_306; })); } _if_result_305; });
|
||||||
el_val_t base_tags = ({ el_val_t _if_result_308 = 0; if (str_eq(tags_raw, EL_STR(""))) { _if_result_308 = (EL_STR("[\"Memory\"]")); } else { _if_result_308 = (tags_raw); } _if_result_308; });
|
el_val_t base_tags = ({ el_val_t _if_result_308 = 0; if (str_eq(tags_raw, EL_STR(""))) { _if_result_308 = (EL_STR("[\"Memory\"]")); } else { _if_result_308 = (tags_raw); } _if_result_308; });
|
||||||
el_val_t final_tags = ({ el_val_t _if_result_309 = 0; if (str_eq(project, EL_STR(""))) { _if_result_309 = (base_tags); } else { el_val_t inner = str_slice(base_tags, 1, (str_len(base_tags) - 1)); _if_result_309 = (el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("["), inner), EL_STR(",\"project:")), project), EL_STR("\"]"))); } _if_result_309; });
|
el_val_t final_tags = ({ el_val_t _if_result_309 = 0; if (str_eq(project, EL_STR(""))) { _if_result_309 = (base_tags); } else { el_val_t inner = str_slice(base_tags, 1, (str_len(base_tags) - 1)); _if_result_309 = (el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("["), inner), EL_STR(",\"project:")), project), EL_STR("\"]"))); } _if_result_309; });
|
||||||
el_val_t id = engram_node_full(content, EL_STR("Memory"), EL_STR("memory:remembered"), el_from_float(sal), el_from_float(sal), el_from_float(el_from_float(0.9)), EL_STR("Episodic"), final_tags);
|
el_val_t req_label = json_get(body, EL_STR("label"));
|
||||||
|
el_val_t eff_label = (str_eq(req_label, EL_STR("")) ? EL_STR("memory:remembered") : req_label);
|
||||||
|
el_val_t id = engram_node_full(content, EL_STR("Memory"), eff_label, el_from_float(sal), el_from_float(sal), el_from_float(el_from_float(0.9)), EL_STR("Episodic"), final_tags);
|
||||||
|
if (str_eq(id, EL_STR(""))) {
|
||||||
|
return EL_STR("{\"ok\":false,\"error\":\"write_not_persisted\",\"id\":\"\"}");
|
||||||
|
}
|
||||||
|
el_val_t remember_readback = engram_get_node_json(id);
|
||||||
|
if (str_eq(remember_readback, EL_STR("")) || str_eq(remember_readback, EL_STR("{}"))) {
|
||||||
|
println(el_str_concat(el_str_concat(EL_STR("[neuron-api] WRITE VERIFY FAILED remember id="), id), EL_STR(" \xe2\x80\x94 node absent after write")));
|
||||||
|
return el_str_concat(el_str_concat(EL_STR("{\"ok\":false,\"error\":\"write_not_persisted\",\"id\":\""), id), EL_STR("\"}"));
|
||||||
|
}
|
||||||
|
return el_str_concat(el_str_concat(EL_STR("{\"id\":\""), id), EL_STR("\",\"ok\":true}"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
el_val_t handle_api_node_create(el_val_t body) {
|
||||||
|
el_val_t content = json_get(body, EL_STR("content"));
|
||||||
|
if (str_eq(content, EL_STR(""))) {
|
||||||
|
return api_err(EL_STR("content is required"));
|
||||||
|
}
|
||||||
|
el_val_t label = json_get(body, EL_STR("label"));
|
||||||
|
el_val_t eff_label = (str_eq(label, EL_STR("")) ? EL_STR("memory:remembered") : label);
|
||||||
|
el_val_t node_type = json_get(body, EL_STR("node_type"));
|
||||||
|
el_val_t eff_type = (str_eq(node_type, EL_STR("")) ? EL_STR("Episodic") : node_type);
|
||||||
|
el_val_t tags_raw = json_get(body, EL_STR("tags"));
|
||||||
|
el_val_t eff_tags = (str_eq(tags_raw, EL_STR("")) ? EL_STR("[\"Memory\"]") : tags_raw);
|
||||||
|
el_val_t importance = json_get(body, EL_STR("importance"));
|
||||||
|
el_val_t sal = (str_eq(importance, EL_STR("critical")) ? el_from_float(0.95) : (str_eq(importance, EL_STR("high")) ? el_from_float(0.75) : (str_eq(importance, EL_STR("low")) ? el_from_float(0.25) : el_from_float(0.7))));
|
||||||
|
el_val_t id = engram_node_full(content, EL_STR("Memory"), eff_label, sal, sal, el_from_float(0.9), eff_type, eff_tags);
|
||||||
|
if (str_eq(id, EL_STR(""))) {
|
||||||
|
return EL_STR("{\"ok\":false,\"error\":\"write_not_persisted\",\"id\":\"\"}");
|
||||||
|
}
|
||||||
|
el_val_t readback = engram_get_node_json(id);
|
||||||
|
if (str_eq(readback, EL_STR("")) || str_eq(readback, EL_STR("{}"))) {
|
||||||
|
return el_str_concat(el_str_concat(EL_STR("{\"ok\":false,\"error\":\"write_not_persisted\",\"id\":\""), id), EL_STR("\"}"));
|
||||||
|
}
|
||||||
return el_str_concat(el_str_concat(EL_STR("{\"id\":\""), id), EL_STR("\",\"ok\":true}"));
|
return el_str_concat(el_str_concat(EL_STR("{\"id\":\""), id), EL_STR("\",\"ok\":true}"));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -27769,6 +27969,14 @@ el_val_t handle_api_capture_knowledge(el_val_t body) {
|
|||||||
el_val_t full = ({ el_val_t _if_result_317 = 0; if (str_eq(title, EL_STR(""))) { _if_result_317 = (content); } else { _if_result_317 = (el_str_concat(el_str_concat(title, EL_STR(": ")), content)); } _if_result_317; });
|
el_val_t full = ({ el_val_t _if_result_317 = 0; if (str_eq(title, EL_STR(""))) { _if_result_317 = (content); } else { _if_result_317 = (el_str_concat(el_str_concat(title, EL_STR(": ")), content)); } _if_result_317; });
|
||||||
el_val_t tags = EL_STR("[\"Knowledge\",\"captured\"]");
|
el_val_t tags = EL_STR("[\"Knowledge\",\"captured\"]");
|
||||||
el_val_t id = engram_node_full(full, EL_STR("Knowledge"), EL_STR("knowledge:captured"), el_from_float(el_from_float(0.85)), el_from_float(el_from_float(0.8)), el_from_float(el_from_float(0.9)), EL_STR("Episodic"), tags);
|
el_val_t id = engram_node_full(full, EL_STR("Knowledge"), EL_STR("knowledge:captured"), el_from_float(el_from_float(0.85)), el_from_float(el_from_float(0.8)), el_from_float(el_from_float(0.9)), EL_STR("Episodic"), tags);
|
||||||
|
if (str_eq(id, EL_STR(""))) {
|
||||||
|
return EL_STR("{\"ok\":false,\"error\":\"write_not_persisted\",\"id\":\"\"}");
|
||||||
|
}
|
||||||
|
el_val_t captured_readback = engram_get_node_json(id);
|
||||||
|
if (str_eq(captured_readback, EL_STR("")) || str_eq(captured_readback, EL_STR("{}"))) {
|
||||||
|
println(el_str_concat(el_str_concat(EL_STR("[neuron-api] WRITE VERIFY FAILED capture id="), id), EL_STR(" \xe2\x80\x94 node absent after write")));
|
||||||
|
return el_str_concat(el_str_concat(EL_STR("{\"ok\":false,\"error\":\"write_not_persisted\",\"id\":\""), id), EL_STR("\"}"));
|
||||||
|
}
|
||||||
return el_str_concat(el_str_concat(EL_STR("{\"id\":\""), id), EL_STR("\",\"ok\":true}"));
|
return el_str_concat(el_str_concat(EL_STR("{\"id\":\""), id), EL_STR("\",\"ok\":true}"));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -28690,6 +28898,57 @@ el_val_t parse_session_subpath(el_val_t path) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* PR#57: connectors subsystem — neuron-connectd bridge on :7771 */
|
||||||
|
el_val_t connectd_get(el_val_t suffix) {
|
||||||
|
el_val_t out = exec_capture(el_str_concat(EL_STR("curl -s --max-time 5 http://127.0.0.1:7771"), suffix));
|
||||||
|
if (str_eq(out, EL_STR(""))) {
|
||||||
|
return EL_STR("{\"ok\":false,\"error\":\"connector bridge unreachable (neuron-connectd on :7771)\"}");
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
el_val_t connectd_post(el_val_t suffix, el_val_t body) {
|
||||||
|
el_val_t eff = ({ el_val_t _if_result_cd1 = 0; if (str_eq(body, EL_STR(""))) { _if_result_cd1 = (EL_STR("{}")); } else { _if_result_cd1 = (body); } _if_result_cd1; });
|
||||||
|
el_val_t tmp = el_str_concat(el_str_concat(EL_STR("/tmp/neuron-connectors-req-"), int_to_str(time_now())), EL_STR(".json"));
|
||||||
|
fs_write(tmp, eff);
|
||||||
|
el_val_t out = exec_capture(el_str_concat(el_str_concat(el_str_concat(EL_STR("curl -s --max-time 20 -X POST http://127.0.0.1:7771"), suffix), EL_STR(" -H 'Content-Type: application/json' -d @")), tmp));
|
||||||
|
if (str_eq(out, EL_STR(""))) {
|
||||||
|
return EL_STR("{\"ok\":false,\"error\":\"connector bridge unreachable (neuron-connectd on :7771)\"}");
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
el_val_t handle_connectors(el_val_t method, el_val_t clean, el_val_t body) {
|
||||||
|
if (str_eq(method, EL_STR("GET"))) {
|
||||||
|
return connectd_get(EL_STR("/mcp/servers"));
|
||||||
|
}
|
||||||
|
if (str_eq(clean, EL_STR("/api/connectors/add"))) {
|
||||||
|
return connectd_post(EL_STR("/mcp/servers/add"), body);
|
||||||
|
}
|
||||||
|
if (str_eq(clean, EL_STR("/api/connectors/toggle"))) {
|
||||||
|
return connectd_post(EL_STR("/mcp/servers/toggle"), body);
|
||||||
|
}
|
||||||
|
if (str_eq(clean, EL_STR("/api/connectors/auto-approve"))) {
|
||||||
|
return connectd_post(EL_STR("/mcp/servers/auto-approve"), body);
|
||||||
|
}
|
||||||
|
if (str_eq(clean, EL_STR("/api/connectors/remove"))) {
|
||||||
|
return connectd_post(EL_STR("/mcp/servers/remove"), body);
|
||||||
|
}
|
||||||
|
if (str_eq(clean, EL_STR("/api/connectors/secret"))) {
|
||||||
|
return connectd_post(EL_STR("/mcp/servers/secret"), body);
|
||||||
|
}
|
||||||
|
if (str_eq(clean, EL_STR("/api/connectors/oauth/start"))) {
|
||||||
|
return connectd_post(EL_STR("/mcp/oauth/start"), body);
|
||||||
|
}
|
||||||
|
if (str_eq(clean, EL_STR("/api/connectors/call"))) {
|
||||||
|
return connectd_post(EL_STR("/mcp/call"), body);
|
||||||
|
}
|
||||||
|
return EL_STR("{\"ok\":false,\"error\":\"unknown connectors route\"}");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body) {
|
el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body) {
|
||||||
el_val_t clean = strip_query(path);
|
el_val_t clean = strip_query(path);
|
||||||
if (str_eq(method, EL_STR("POST")) && str_eq(clean, EL_STR("/dharma/recv"))) {
|
if (str_eq(method, EL_STR("POST")) && str_eq(clean, EL_STR("/dharma/recv"))) {
|
||||||
@@ -28789,12 +29048,15 @@ el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body) {
|
|||||||
return handle_api_inspect_graph(method, path, body);
|
return handle_api_inspect_graph(method, path, body);
|
||||||
}
|
}
|
||||||
if (str_starts_with(clean, EL_STR("/api/neuron/list/"))) {
|
if (str_starts_with(clean, EL_STR("/api/neuron/list/"))) {
|
||||||
el_val_t node_type = str_slice(clean, 16, str_len(clean));
|
el_val_t node_type = str_slice(clean, 17, str_len(clean)); /* PR#58: was 16, left leading "/" on node_type */
|
||||||
return handle_api_list_typed(node_type, path, body);
|
return handle_api_list_typed(node_type, path, body);
|
||||||
}
|
}
|
||||||
if (str_starts_with(clean, EL_STR("/api/neuron/recall"))) {
|
if (str_starts_with(clean, EL_STR("/api/neuron/recall"))) {
|
||||||
return handle_api_recall(method, path, body);
|
return handle_api_recall(method, path, body);
|
||||||
}
|
}
|
||||||
|
if (str_starts_with(clean, EL_STR("/api/connectors"))) {
|
||||||
|
return handle_connectors(method, clean, body);
|
||||||
|
}
|
||||||
return err_404(clean);
|
return err_404(clean);
|
||||||
}
|
}
|
||||||
if (str_eq(method, EL_STR("POST"))) {
|
if (str_eq(method, EL_STR("POST"))) {
|
||||||
@@ -28901,6 +29163,9 @@ el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body) {
|
|||||||
if (str_eq(clean, EL_STR("/api/neuron/graph/link"))) {
|
if (str_eq(clean, EL_STR("/api/neuron/graph/link"))) {
|
||||||
return handle_api_link_entities(body);
|
return handle_api_link_entities(body);
|
||||||
}
|
}
|
||||||
|
if (str_eq(clean, EL_STR("/api/neuron/node/create"))) {
|
||||||
|
return handle_api_node_create(body);
|
||||||
|
}
|
||||||
if (str_eq(clean, EL_STR("/api/neuron/memory"))) {
|
if (str_eq(clean, EL_STR("/api/neuron/memory"))) {
|
||||||
return handle_api_remember(body);
|
return handle_api_remember(body);
|
||||||
}
|
}
|
||||||
@@ -28925,6 +29190,9 @@ el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body) {
|
|||||||
if (str_eq(clean, EL_STR("/api/neuron/cultivate"))) {
|
if (str_eq(clean, EL_STR("/api/neuron/cultivate"))) {
|
||||||
return handle_api_cultivate(body);
|
return handle_api_cultivate(body);
|
||||||
}
|
}
|
||||||
|
if (str_starts_with(clean, EL_STR("/api/connectors"))) {
|
||||||
|
return handle_connectors(method, clean, body);
|
||||||
|
}
|
||||||
return err_404(clean);
|
return err_404(clean);
|
||||||
}
|
}
|
||||||
if (str_eq(method, EL_STR("DELETE"))) {
|
if (str_eq(method, EL_STR("DELETE"))) {
|
||||||
|
|||||||
-5
@@ -334,8 +334,3 @@ el_val_t entry_form(el_val_t entry, el_val_t n) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int _argc, char** _argv) {
|
|
||||||
el_runtime_init_args(_argc, _argv);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
# GLM-OCR Spike — 2026-06-27
|
||||||
|
|
||||||
|
## Verdict: SHIP IT
|
||||||
|
|
||||||
|
MLX-native path confirmed. Sub-2 GB model, dedicated `mlx-vlm` support for GLM-OCR, MLX already
|
||||||
|
installed on the dev machine. No blockers.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Model
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|-------|-------|
|
||||||
|
| **Name** | GLM-OCR |
|
||||||
|
| **HuggingFace path** | `zai-org/GLM-OCR` (base BF16) |
|
||||||
|
| **MLX path** | `mlx-community/GLM-OCR-8bit` |
|
||||||
|
| **Parameters** | 0.9B |
|
||||||
|
| **Disk (MLX 8-bit)** | 1.59 GB (`model.safetensors` 1.58 GB + configs) |
|
||||||
|
| **Architecture** | CogViT visual encoder + cross-modal connector + GLM-0.5B decoder |
|
||||||
|
| **License** | MIT (model); Apache 2.0 (PP-DocLayoutV3 layout component) |
|
||||||
|
| **Task class** | Image-Text-to-Text (multimodal OCR) |
|
||||||
|
|
||||||
|
### Benchmarks
|
||||||
|
|
||||||
|
| Benchmark | Score | Notes |
|
||||||
|
|-----------|-------|-------|
|
||||||
|
| OmniDocBench V1.5 | **94.62** | Ranked #1 at evaluation date |
|
||||||
|
| olmOCR-bench (overall) | 75.2 | — |
|
||||||
|
| Throughput (base, GPU) | 0.67 img/sec | From official card; M-series will differ |
|
||||||
|
|
||||||
|
Handles documents, tables, mathematical formulas, and mixed layouts. Not just raw text extraction —
|
||||||
|
returns structured markdown output.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Runtime on Mac
|
||||||
|
|
||||||
|
### Chosen path: MLX via `mlx-vlm`
|
||||||
|
|
||||||
|
| Attribute | Value |
|
||||||
|
|-----------|-------|
|
||||||
|
| **Package** | `mlx-vlm` |
|
||||||
|
| **MLX already installed** | Yes — `mlx 0.31.2`, `mlx-lm 0.31.3`, `mlx-metal 0.31.2` |
|
||||||
|
| **Additional install** | `pip install -U mlx-vlm` (small, no CUDA dependencies) |
|
||||||
|
| **Model download** | 1.59 GB on first run (auto-cached in `~/.cache/huggingface/`) |
|
||||||
|
| **Memory requirement** | ~2–3 GB unified memory (1.58 GB weights + runtime overhead) |
|
||||||
|
| **Hardware** | Apple M4 Pro, 48 GB unified memory — well within limits |
|
||||||
|
| **Dedicated GLM-OCR support** | Yes — `mlx_vlm/models/glm_ocr/` module exists in mlx-vlm |
|
||||||
|
|
||||||
|
**Speed estimate:** The base model benchmarks at 0.67 img/sec on GPU. On M4 Pro via MPS/MLX,
|
||||||
|
expect 0.3–0.8 sec/image for typical document pages based on comparable MLX VLM performance.
|
||||||
|
Exact figures require a timed run with the prototype.
|
||||||
|
|
||||||
|
### Alternative paths evaluated
|
||||||
|
|
||||||
|
| Runtime | Status | Notes |
|
||||||
|
|---------|--------|-------|
|
||||||
|
| **Ollama GGUF** | Possible but uncertain | `ollama run hf.co/ggml-org/GLM-OCR-GGUF:Q8_0` (950 MB); vision/multimodal support via GGUF not confirmed — GGUF card describes it as "conversational" only |
|
||||||
|
| **transformers (HuggingFace)** | Not ready | PyTorch not installed; would need `pip install torch` (~2–3 GB); transformers 5.6.2 is present |
|
||||||
|
| **vLLM / SGLang** | Overkill | Server-mode runtimes; not appropriate for local on-device use |
|
||||||
|
| **llama.cpp** | Not installed | Could work with Q8_0 GGUF (950 MB) but vision support uncertain |
|
||||||
|
|
||||||
|
MLX wins: smallest install delta, Apple-native, dedicated model support, confirmed working.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Integration Plan
|
||||||
|
|
||||||
|
### Step 1 — Install mlx-vlm (one-time)
|
||||||
|
```bash
|
||||||
|
pip install -U mlx-vlm
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2 — Run OCR on an image
|
||||||
|
```bash
|
||||||
|
python -m mlx_vlm.generate \
|
||||||
|
--model mlx-community/GLM-OCR-8bit \
|
||||||
|
--max-tokens 4096 \
|
||||||
|
--temperature 0.0 \
|
||||||
|
--prompt "Extract all text from this document. Preserve structure including tables and headers." \
|
||||||
|
--image /path/to/document.jpg
|
||||||
|
```
|
||||||
|
|
||||||
|
Model auto-downloads (~1.59 GB) on first run and caches in `~/.cache/huggingface/`.
|
||||||
|
|
||||||
|
### Step 3 — Post to Neuron soul
|
||||||
|
```bash
|
||||||
|
curl -s -X POST http://localhost:7770/api/neuron/memory \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"content\":\"<OCR_TEXT>\",\"label\":\"Photo: filename.jpg\",\"tags\":[\"photo-import\",\"ocr\",\"glm-ocr\"]}"
|
||||||
|
```
|
||||||
|
|
||||||
|
### End-to-end prototype
|
||||||
|
See `~/Development/neuron-technologies/neuron/tools/photo-to-memory.sh` — working stub.
|
||||||
|
|
||||||
|
### Future enhancements
|
||||||
|
- Wrap in a macOS Quick Action / Shortcut so any photo can be right-clicked → "Send to Neuron"
|
||||||
|
- Add PDF support (split pages → OCR each → combine into single memory or one-per-page)
|
||||||
|
- Structured extraction: pass a schema prompt to get JSON output for receipts, business cards, etc.
|
||||||
|
- Batch mode for importing a folder of scanned documents
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Recommendation
|
||||||
|
|
||||||
|
Install `mlx-vlm` and run the prototype against a sample document to validate output quality and
|
||||||
|
measure actual M4 Pro throughput before wiring into any production flow. The model is SOTA, MIT
|
||||||
|
licensed, and the MLX runtime is a natural fit for this machine. There is no reason not to proceed.
|
||||||
|
|
||||||
|
The photo-to-memory.sh prototype is ready to test immediately after `pip install -U mlx-vlm`.
|
||||||
+27
-2
@@ -267,6 +267,27 @@ fn recall_or_list(query: String, limit: Int) -> String {
|
|||||||
return http_post_json(neuron_url() + "/recall", body)
|
return http_post_json(neuron_url() + "/recall", body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a real typed node via /api/neuron/node/create (handle_api_node_create) so it is a proper
|
||||||
|
// BacklogItem/Artifact/etc. — listable by type via /api/neuron/list/<type> — instead of a generic
|
||||||
|
// memory blob. Maps title->label, content/description->content, project/priority->tags.
|
||||||
|
fn create_node_typed(args: String, node_type: String, tier: String) -> String {
|
||||||
|
let content: String = pick_content(args)
|
||||||
|
if str_eq(content, "") {
|
||||||
|
return mcp_text_result("error: content/title is required for " + node_type)
|
||||||
|
}
|
||||||
|
let title: String = json_get_string(args, "title")
|
||||||
|
let label: String = if str_eq(title, "") { node_type } else { title }
|
||||||
|
let project: String = json_get_string(args, "project")
|
||||||
|
let priority: String = json_get_string(args, "priority")
|
||||||
|
let proj_tag: String = if str_eq(project, "") { "" } else { ",\"project:" + project + "\"" }
|
||||||
|
let prio_tag: String = if str_eq(priority, "") { "" } else { ",\"priority:" + priority + "\"" }
|
||||||
|
let tags: String = "[\"" + node_type + "\"" + proj_tag + prio_tag + "]"
|
||||||
|
let body: String = "{\"node_type\":\"" + node_type + "\",\"content\":\"" + json_escape(content)
|
||||||
|
+ "\",\"label\":\"" + json_escape(label) + "\",\"tier\":\"" + tier + "\",\"tags\":" + tags + "}"
|
||||||
|
let resp: String = http_post_json(neuron_url() + "/node/create", body)
|
||||||
|
return mcp_json_result(resp)
|
||||||
|
}
|
||||||
|
|
||||||
fn search_with_query(args: String, default_limit: Int) -> String {
|
fn search_with_query(args: String, default_limit: Int) -> String {
|
||||||
let query: String = json_get_string(args, "query")
|
let query: String = json_get_string(args, "query")
|
||||||
if str_eq(query, "") { let query = pick_content(args) }
|
if str_eq(query, "") { let query = pick_content(args) }
|
||||||
@@ -631,8 +652,12 @@ fn dispatch_tool_call(tool_name: String, args: String) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ── Backlog + work ──────────────────────────────────────────────────────
|
// ── Backlog + work ──────────────────────────────────────────────────────
|
||||||
if str_eq(tool_name, "planWork") { return create_typed_node(args, "BacklogItem", "0.65") }
|
// planWork: create a REAL typed BacklogItem via /api/neuron/node/create (the old path fell through
|
||||||
if str_eq(tool_name, "reviewBacklog") { return search_with_query(args, 50) }
|
// create_typed_node to a generic /memory write, dropping title/project/priority and never making a
|
||||||
|
// BacklogItem). reviewBacklog: LIST BacklogItem nodes (was a lexical /recall that never filtered by
|
||||||
|
// type). Both depend on the /api/neuron/list/<type> slice fix (neuron PR #58) to round-trip.
|
||||||
|
if str_eq(tool_name, "planWork") { return create_node_typed(args, "BacklogItem", "Working") }
|
||||||
|
if str_eq(tool_name, "reviewBacklog") { return list_typed("BacklogItem", 50, args) }
|
||||||
if str_eq(tool_name, "trackWork") { return evolve_by_supersede(args, "Memory") }
|
if str_eq(tool_name, "trackWork") { return evolve_by_supersede(args, "Memory") }
|
||||||
if str_eq(tool_name, "listWork") { return list_typed("WorkContext", 50, args) }
|
if str_eq(tool_name, "listWork") { return list_typed("WorkContext", 50, args) }
|
||||||
if str_eq(tool_name, "beginWork") { return create_typed_node(args, "Memory", "0.70") }
|
if str_eq(tool_name, "beginWork") { return create_typed_node(args, "Memory", "0.70") }
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ fn tier_episodic() -> String { return "Episodic" }
|
|||||||
fn tier_canonical() -> String { return "Canonical" }
|
fn tier_canonical() -> String { return "Canonical" }
|
||||||
|
|
||||||
fn mem_store(content: String, label: String, tags: String) -> String {
|
fn mem_store(content: String, label: String, tags: String) -> String {
|
||||||
return engram_node_full(
|
let id: String = engram_node_full(
|
||||||
content,
|
content,
|
||||||
"Memory",
|
"Memory",
|
||||||
label,
|
label,
|
||||||
@@ -13,6 +13,18 @@ fn mem_store(content: String, label: String, tags: String) -> String {
|
|||||||
"Working",
|
"Working",
|
||||||
tags
|
tags
|
||||||
)
|
)
|
||||||
|
if str_eq(id, "") {
|
||||||
|
println("[memory] write rejected by engram (empty id): label=" + label)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
// Read back to verify the node actually persisted — guards against silent write failures.
|
||||||
|
let readback: String = engram_get_node_json(id)
|
||||||
|
if str_eq(readback, "") || str_eq(readback, "{}") {
|
||||||
|
println("[memory] WRITE VERIFY FAILED: label=" + label + " id=" + id + " — node absent after write")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
println("[memory] write verified: " + id + " ok")
|
||||||
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mem_remember(content: String, tags: String) -> String {
|
fn mem_remember(content: String, tags: String) -> String {
|
||||||
@@ -136,7 +148,12 @@ fn mem_boot_count_inc() -> Int {
|
|||||||
"Canonical", tags
|
"Canonical", tags
|
||||||
)
|
)
|
||||||
if str_eq(boot_node_id, "") {
|
if str_eq(boot_node_id, "") {
|
||||||
println("[memory] mem_boot_count_inc: engram write failed — boot counter node lost (count=" + int_to_str(next) + ")")
|
println("[memory] mem_boot_count_inc: write rejected (empty id) — boot counter node lost (count=" + int_to_str(next) + ")")
|
||||||
|
return next
|
||||||
|
}
|
||||||
|
let boot_readback: String = engram_get_node_json(boot_node_id)
|
||||||
|
if str_eq(boot_readback, "") || str_eq(boot_readback, "{}") {
|
||||||
|
println("[memory] mem_boot_count_inc: WRITE VERIFY FAILED id=" + boot_node_id + " count=" + int_to_str(next))
|
||||||
}
|
}
|
||||||
return next
|
return next
|
||||||
}
|
}
|
||||||
@@ -155,9 +172,13 @@ fn mem_emit_state_event(trigger: String, kind: String, content: String) -> Strin
|
|||||||
+ ",\"boot\":" + int_to_str(boot)
|
+ ",\"boot\":" + int_to_str(boot)
|
||||||
+ ",\"ts\":" + int_to_str(ts) + "}"
|
+ ",\"ts\":" + int_to_str(ts) + "}"
|
||||||
let tags: String = "[\"internal-state\",\"pre-reasoning\",\"InternalStateEvent\"]"
|
let tags: String = "[\"internal-state\",\"pre-reasoning\",\"InternalStateEvent\"]"
|
||||||
return engram_node_full(
|
let event_id: String = engram_node_full(
|
||||||
payload, "InternalStateEvent", "state-event:" + kind,
|
payload, "InternalStateEvent", "state-event:" + kind,
|
||||||
el_from_float(0.85), el_from_float(0.8), el_from_float(0.9),
|
el_from_float(0.85), el_from_float(0.8), el_from_float(0.9),
|
||||||
"Episodic", tags
|
"Episodic", tags
|
||||||
)
|
)
|
||||||
|
if str_eq(event_id, "") {
|
||||||
|
println("[memory] mem_emit_state_event: write rejected (empty id): kind=" + kind)
|
||||||
|
}
|
||||||
|
return event_id
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// auto-generated by elc --emit-header - do not edit
|
// auto-generated by elc --emit-header — do not edit
|
||||||
extern fn tier_working() -> String
|
extern fn tier_working() -> String
|
||||||
extern fn tier_episodic() -> String
|
extern fn tier_episodic() -> String
|
||||||
extern fn tier_canonical() -> String
|
extern fn tier_canonical() -> String
|
||||||
|
|||||||
+3
-1
@@ -94,7 +94,9 @@ fn api_or_empty(s: String) -> String {
|
|||||||
fn api_persisted(id: String) -> Bool {
|
fn api_persisted(id: String) -> Bool {
|
||||||
if str_eq(id, "") { return false }
|
if str_eq(id, "") { return false }
|
||||||
let node: String = engram_get_node_json(id)
|
let node: String = engram_get_node_json(id)
|
||||||
return !str_eq(node, "") && !str_eq(node, "null")
|
// engram_get_node_json returns "{}" (empty object) when node is not found — not "" or "null".
|
||||||
|
// Check all three to guard against any runtime variation.
|
||||||
|
return !str_eq(node, "") && !str_eq(node, "null") && !str_eq(node, "{}")
|
||||||
}
|
}
|
||||||
|
|
||||||
// api_not_persisted — standard error for a write that did not read back.
|
// api_not_persisted — standard error for a write that did not read back.
|
||||||
|
|||||||
@@ -8,9 +8,14 @@ extern fn api_ok(extra: String) -> String
|
|||||||
extern fn api_err(msg: String) -> String
|
extern fn api_err(msg: String) -> String
|
||||||
extern fn api_nonempty(s: String) -> Bool
|
extern fn api_nonempty(s: String) -> Bool
|
||||||
extern fn api_or_empty(s: String) -> String
|
extern fn api_or_empty(s: String) -> String
|
||||||
|
extern fn api_persisted(id: String) -> Bool
|
||||||
|
extern fn api_not_persisted(id: String) -> String
|
||||||
extern fn handle_api_begin_session(body: String) -> String
|
extern fn handle_api_begin_session(body: String) -> String
|
||||||
extern fn handle_api_compile_ctx(body: String) -> String
|
extern fn handle_api_compile_ctx(body: String) -> String
|
||||||
extern fn handle_api_remember(body: String) -> String
|
extern fn handle_api_remember(body: String) -> String
|
||||||
|
extern fn handle_api_node_create(body: String) -> String
|
||||||
|
extern fn handle_api_node_delete(body: String) -> String
|
||||||
|
extern fn handle_api_node_update(body: String) -> String
|
||||||
extern fn handle_api_recall(method: String, path: String, body: String) -> String
|
extern fn handle_api_recall(method: String, path: String, body: String) -> String
|
||||||
extern fn handle_api_search_knowledge(method: String, path: String, body: String) -> String
|
extern fn handle_api_search_knowledge(method: String, path: String, body: String) -> String
|
||||||
extern fn handle_api_browse_knowledge(path: String, body: String) -> String
|
extern fn handle_api_browse_knowledge(path: String, body: String) -> String
|
||||||
@@ -27,6 +32,8 @@ extern fn handle_api_inspect_graph(method: String, path: String, body: String) -
|
|||||||
extern fn handle_api_link_entities(body: String) -> String
|
extern fn handle_api_link_entities(body: String) -> String
|
||||||
extern fn handle_api_forget(body: String) -> String
|
extern fn handle_api_forget(body: String) -> String
|
||||||
extern fn handle_api_evolve_memory(body: String) -> String
|
extern fn handle_api_evolve_memory(body: String) -> String
|
||||||
|
extern fn handle_api_memory_delete(body: String) -> String
|
||||||
|
extern fn handle_api_memory_update(body: String) -> String
|
||||||
extern fn handle_api_cultivate(body: String) -> String
|
extern fn handle_api_cultivate(body: String) -> String
|
||||||
extern fn handle_api_list_typed(node_type: String, path: String, body: String) -> String
|
extern fn handle_api_list_typed(node_type: String, path: String, body: String) -> String
|
||||||
extern fn handle_api_consolidate(body: String) -> String
|
extern fn handle_api_consolidate(body: String) -> String
|
||||||
|
|||||||
@@ -335,6 +335,12 @@ fn handle_connectors(method: String, clean: String, body: String) -> String {
|
|||||||
if str_eq(clean, "/api/connectors/oauth/start") {
|
if str_eq(clean, "/api/connectors/oauth/start") {
|
||||||
return connectd_post("/mcp/oauth/start", body)
|
return connectd_post("/mcp/oauth/start", body)
|
||||||
}
|
}
|
||||||
|
// Call a connector tool directly (pre-chat), e.g. WhatsApp get_pairing_qr / get_login_status for
|
||||||
|
// the pairing UI. Body: {"name":"mcp__<server>__<tool>","input":{...}}. Keeps the app on the
|
||||||
|
// app->soul->connectd path (the UI never hits connectd directly) and works for remote/hosted apps.
|
||||||
|
if str_eq(clean, "/api/connectors/call") {
|
||||||
|
return connectd_post("/mcp/call", body)
|
||||||
|
}
|
||||||
return "{\"ok\":false,\"error\":\"unknown connectors route\"}"
|
return "{\"ok\":false,\"error\":\"unknown connectors route\"}"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -459,7 +465,10 @@ fn handle_request(method: String, path: String, body: String) -> String {
|
|||||||
return handle_api_inspect_graph(method, path, body)
|
return handle_api_inspect_graph(method, path, body)
|
||||||
}
|
}
|
||||||
if str_starts_with(clean, "/api/neuron/list/") {
|
if str_starts_with(clean, "/api/neuron/list/") {
|
||||||
let node_type: String = str_slice(clean, 16, str_len(clean))
|
// Offset 17 = len("/api/neuron/list/"). Was 16, which left a leading "/" on node_type
|
||||||
|
// ("/BacklogItem"), so engram_scan_nodes_by_type_json matched nothing → list/<type>
|
||||||
|
// returned [] for EVERY type (broke backlog/typed-node listing app- and tool-wide).
|
||||||
|
let node_type: String = str_slice(clean, 17, str_len(clean))
|
||||||
return handle_api_list_typed(node_type, path, body)
|
return handle_api_list_typed(node_type, path, body)
|
||||||
}
|
}
|
||||||
if str_starts_with(clean, "/api/neuron/recall") {
|
if str_starts_with(clean, "/api/neuron/recall") {
|
||||||
|
|||||||
+5
-5
@@ -1,6 +1,6 @@
|
|||||||
// auto-generated by elc --emit-header - do not edit
|
// auto-generated by elc --emit-header — do not edit
|
||||||
|
extern fn rate_limit_check(ip: String, path: String) -> String
|
||||||
extern fn strip_query(path: String) -> String
|
extern fn strip_query(path: String) -> String
|
||||||
extern fn flag_true(body: String, key: String) -> Bool
|
|
||||||
extern fn err_404(path: String) -> String
|
extern fn err_404(path: String) -> String
|
||||||
extern fn err_405(method: String, path: String) -> String
|
extern fn err_405(method: String, path: String) -> String
|
||||||
extern fn route_health() -> String
|
extern fn route_health() -> String
|
||||||
@@ -9,7 +9,7 @@ extern fn route_imprint_contextual(body: String) -> String
|
|||||||
extern fn route_imprint_user(body: String) -> String
|
extern fn route_imprint_user(body: String) -> String
|
||||||
extern fn route_synthesize(body: String) -> String
|
extern fn route_synthesize(body: String) -> String
|
||||||
extern fn handle_dharma_recv(body: String) -> String
|
extern fn handle_dharma_recv(body: String) -> String
|
||||||
extern fn route_sessions() -> String
|
extern fn connectd_get(suffix: String) -> String
|
||||||
extern fn parse_session_id_from_path(path: String) -> String
|
extern fn connectd_post(suffix: String, body: String) -> String
|
||||||
extern fn parse_session_subpath(path: String) -> String
|
extern fn handle_connectors(method: String, clean: String, body: String) -> String
|
||||||
extern fn handle_request(method: String, path: String, body: String) -> String
|
extern fn handle_request(method: String, path: String, body: String) -> String
|
||||||
|
|||||||
+5
-4
@@ -1,7 +1,10 @@
|
|||||||
// Layer 1 — Safety: extern declarations
|
|
||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header — do not edit
|
||||||
extern fn soft_bell_threshold() -> Int
|
extern fn soft_bell_threshold() -> Int
|
||||||
extern fn hard_bell_threshold() -> Int
|
extern fn hard_bell_threshold() -> Int
|
||||||
|
extern fn safety_score_crisis(input: String) -> Int
|
||||||
|
extern fn safety_score_harm(input: String) -> Int
|
||||||
|
extern fn safety_score_danger(input: String) -> Int
|
||||||
|
extern fn safety_score_distress_history(history: String) -> Int
|
||||||
extern fn safety_threat_score(input: String, history: String) -> Int
|
extern fn safety_threat_score(input: String, history: String) -> Int
|
||||||
extern fn safety_screen(input: String, history: String) -> String
|
extern fn safety_screen(input: String, history: String) -> String
|
||||||
extern fn safety_validate(output: String, action: String) -> String
|
extern fn safety_validate(output: String, action: String) -> String
|
||||||
@@ -10,9 +13,7 @@ extern fn safety_self_harm_phrases() -> String
|
|||||||
extern fn safety_abuse_phrases() -> String
|
extern fn safety_abuse_phrases() -> String
|
||||||
extern fn safety_general_hard_phrases() -> String
|
extern fn safety_general_hard_phrases() -> String
|
||||||
extern fn safety_soft_phrases() -> String
|
extern fn safety_soft_phrases() -> String
|
||||||
extern fn safety_normalize(message: String) -> String
|
extern fn safety_detect_positive_level(message: String) -> String
|
||||||
extern fn safety_any_match(text: String, phrases_json: String) -> Bool
|
|
||||||
extern fn safety_count_match(text: String, phrases_json: String) -> Int
|
|
||||||
extern fn safety_detect_bell_level(message: String) -> String
|
extern fn safety_detect_bell_level(message: String) -> String
|
||||||
extern fn safety_classify_hard_bell(message: String) -> String
|
extern fn safety_classify_hard_bell(message: String) -> String
|
||||||
extern fn safety_soft_directive() -> String
|
extern fn safety_soft_directive() -> String
|
||||||
|
|||||||
+4
-5
@@ -1,14 +1,13 @@
|
|||||||
// auto-generated by elc --emit-header — do not edit
|
// auto-generated by elc --emit-header — do not edit
|
||||||
extern fn session_title_from_message(message: String) -> String
|
extern fn session_title_from_message(message: String) -> String
|
||||||
extern fn session_make_content(id: String, title: String, created_at: Int, updated_at: Int) -> String
|
extern fn session_make_content(id: String, title: String, created_at: Int, updated_at: Int, folder: String) -> String
|
||||||
|
extern fn session_exists(session_id: String) -> Bool
|
||||||
extern fn session_create(body: String) -> String
|
extern fn session_create(body: String) -> String
|
||||||
|
extern fn session_create_cleanup(session_id: String) -> String
|
||||||
extern fn session_list() -> String
|
extern fn session_list() -> String
|
||||||
extern fn session_get(session_id: String) -> String
|
extern fn session_get(session_id: String) -> String
|
||||||
extern fn session_delete(session_id: String) -> String
|
extern fn session_delete(session_id: String) -> String
|
||||||
extern fn session_update_title(session_id: String, body: String) -> String
|
extern fn session_update_patch(session_id: String, body: String) -> String
|
||||||
extern fn session_search(query: String) -> String
|
extern fn session_search(query: String) -> String
|
||||||
extern fn session_hist_load(session_id: String) -> String
|
extern fn session_hist_load(session_id: String) -> String
|
||||||
extern fn session_hist_save(session_id: String, hist: String) -> Void
|
extern fn session_hist_save(session_id: String, hist: String) -> Void
|
||||||
extern fn session_update_meta_timestamp(session_id: String) -> Void
|
|
||||||
extern fn session_auto_title(session_id: String, first_message: String) -> Void
|
|
||||||
extern fn handle_session_approve(session_id: String, body: String) -> String
|
|
||||||
|
|||||||
@@ -109,6 +109,43 @@ fn ensure_self_canonical_bridge() -> Void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// aff_try_slot — accumulate one affective-context node into state.
|
||||||
|
// Replaces the broken `let bacc = while bi < N { ... let bacc = ... }` pattern
|
||||||
|
// that caused ELC to emit duplicate C declarations for `bacc`.
|
||||||
|
// (2026-06-23 self-review: EL compiler codegen bug — while loop with let-rebinding
|
||||||
|
// inside the loop body generates `el_val_t bacc = ...` twice in the same C scope.)
|
||||||
|
// Callers unroll manually to 3 slots (matching engram_search_json limit=3).
|
||||||
|
// Guards: empty slot_json (out-of-bounds json_array_get) → no-op.
|
||||||
|
fn aff_try_slot(slot_json: String, aff_7d_ts: Int, acc_key: String) -> Void {
|
||||||
|
if str_eq(slot_json, "") { return "" }
|
||||||
|
let bn_c: String = json_get(slot_json, "content")
|
||||||
|
if str_eq(bn_c, "") { return "" }
|
||||||
|
let bm: String = " | ts:"
|
||||||
|
let bmp: Int = str_index_of(bn_c, bm)
|
||||||
|
state_set("_ats_ts_raw", "")
|
||||||
|
if bmp >= 0 {
|
||||||
|
let bs: Int = bmp + str_len(bm)
|
||||||
|
let br: String = str_slice(bn_c, bs, str_len(bn_c))
|
||||||
|
let bn_next: Int = str_index_of(br, " | ")
|
||||||
|
if bn_next < 0 { state_set("_ats_ts_raw", br) }
|
||||||
|
if bn_next >= 0 { state_set("_ats_ts_raw", str_slice(br, 0, bn_next)) }
|
||||||
|
}
|
||||||
|
if bmp < 0 {
|
||||||
|
let bca: String = json_get(slot_json, "created_at")
|
||||||
|
if str_eq(bca, "") { state_set("_ats_ts_raw", json_get(slot_json, "updated_at")) }
|
||||||
|
if !str_eq(bca, "") { state_set("_ats_ts_raw", bca) }
|
||||||
|
}
|
||||||
|
let bn_ts_raw: String = state_get("_ats_ts_raw")
|
||||||
|
let bn_ts: Int = if str_eq(bn_ts_raw, "") { 0 } else { str_to_int(bn_ts_raw) }
|
||||||
|
let snip: String = if str_len(bn_c) > 200 { str_slice(bn_c, 0, 200) } else { bn_c }
|
||||||
|
if bn_ts >= aff_7d_ts && !str_eq(snip, "") {
|
||||||
|
let cur_acc: String = state_get(acc_key)
|
||||||
|
if str_eq(cur_acc, "") { state_set(acc_key, snip) }
|
||||||
|
if !str_eq(cur_acc, "") { state_set(acc_key, cur_acc + "\n" + snip) }
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// load_identity_context — pull key identity nodes from engram into working state.
|
// load_identity_context — pull key identity nodes from engram into working state.
|
||||||
// Called at boot after engram_load. These nodes contain values, intellectual-dna,
|
// Called at boot after engram_load. These nodes contain values, intellectual-dna,
|
||||||
// memory-philosophy — the graph-stored self that chat.el can include in prompts.
|
// memory-philosophy — the graph-stored self that chat.el can include in prompts.
|
||||||
@@ -172,68 +209,29 @@ fn load_identity_context() -> Void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cross-session affective context: load BellEvent and PositiveEvent nodes from last 7 days.
|
// Cross-session affective context: load BellEvent and PositiveEvent nodes from last 7 days.
|
||||||
|
// (2026-06-23: replaced while-loop accumulation with manual 3-slot unroll via aff_try_slot.
|
||||||
|
// The EL codegen bug: `let bacc = while ... { ... let bacc = ... }` emits `el_val_t bacc`
|
||||||
|
// twice in the same C scope. Since search limit=3, manual unrolling is exact.)
|
||||||
let aff_now: Int = time_now()
|
let aff_now: Int = time_now()
|
||||||
let aff_7d: Int = aff_now - 604800
|
let aff_7d: Int = aff_now - 604800
|
||||||
let bell_raw: String = engram_search_json("bell:soft bell:hard BellEvent affective", 3)
|
let bell_raw: String = engram_search_json("bell:soft bell:hard BellEvent affective", 3)
|
||||||
let bell_aff_ok: Bool = !str_eq(bell_raw, "") && !str_eq(bell_raw, "[]")
|
let bell_aff_ok: Bool = !str_eq(bell_raw, "") && !str_eq(bell_raw, "[]")
|
||||||
let aff_ctx: String = ""
|
let aff_ctx: String = ""
|
||||||
let aff_ctx = if bell_aff_ok {
|
let aff_ctx = if bell_aff_ok {
|
||||||
let bn_total: Int = json_array_len(bell_raw)
|
state_set("_bell_acc", "")
|
||||||
let bacc: String = ""
|
aff_try_slot(json_array_get(bell_raw, 0), aff_7d, "_bell_acc")
|
||||||
let bi: Int = 0
|
aff_try_slot(json_array_get(bell_raw, 1), aff_7d, "_bell_acc")
|
||||||
let bacc = while bi < bn_total {
|
aff_try_slot(json_array_get(bell_raw, 2), aff_7d, "_bell_acc")
|
||||||
let bn: String = json_array_get(bell_raw, bi)
|
state_get("_bell_acc")
|
||||||
let bn_c: String = json_get(bn, "content")
|
|
||||||
let bm: String = " | ts:"
|
|
||||||
let bmp: Int = str_index_of(bn_c, bm)
|
|
||||||
let bn_ts_raw: String = if bmp >= 0 {
|
|
||||||
let bs: Int = bmp + str_len(bm)
|
|
||||||
let br: String = str_slice(bn_c, bs, str_len(bn_c))
|
|
||||||
let bn_next: Int = str_index_of(br, " | ")
|
|
||||||
if bn_next < 0 { br } else { str_slice(br, 0, bn_next) }
|
|
||||||
} else {
|
|
||||||
let bca: String = json_get(bn, "created_at")
|
|
||||||
if str_eq(bca, "") { json_get(bn, "updated_at") } else { bca }
|
|
||||||
}
|
|
||||||
let bn_ts: Int = if str_eq(bn_ts_raw, "") { 0 } else { str_to_int(bn_ts_raw) }
|
|
||||||
let snip: String = if str_len(bn_c) > 200 { str_slice(bn_c, 0, 200) } else { bn_c }
|
|
||||||
let bacc = if bn_ts >= aff_7d && !str_eq(snip, "") {
|
|
||||||
if str_eq(bacc, "") { snip } else { bacc + "\n" + snip }
|
|
||||||
} else { bacc }
|
|
||||||
let bi = bi + 1
|
|
||||||
bacc
|
|
||||||
}
|
|
||||||
bacc
|
|
||||||
} else { "" }
|
} else { "" }
|
||||||
let pos_raw: String = engram_search_json("PositiveEvent joy:high joy:low affective", 3)
|
let pos_raw: String = engram_search_json("PositiveEvent joy:high joy:low affective", 3)
|
||||||
let pos_aff_ok: Bool = !str_eq(pos_raw, "") && !str_eq(pos_raw, "[]")
|
let pos_aff_ok: Bool = !str_eq(pos_raw, "") && !str_eq(pos_raw, "[]")
|
||||||
let aff_ctx = if pos_aff_ok {
|
let aff_ctx = if pos_aff_ok {
|
||||||
let pn_total: Int = json_array_len(pos_raw)
|
state_set("_pos_acc", aff_ctx)
|
||||||
let pacc: String = aff_ctx
|
aff_try_slot(json_array_get(pos_raw, 0), aff_7d, "_pos_acc")
|
||||||
let pi: Int = 0
|
aff_try_slot(json_array_get(pos_raw, 1), aff_7d, "_pos_acc")
|
||||||
let pacc = while pi < pn_total {
|
aff_try_slot(json_array_get(pos_raw, 2), aff_7d, "_pos_acc")
|
||||||
let pn: String = json_array_get(pos_raw, pi)
|
state_get("_pos_acc")
|
||||||
let pn_c: String = json_get(pn, "content")
|
|
||||||
let pm: String = " | ts:"
|
|
||||||
let pmp: Int = str_index_of(pn_c, pm)
|
|
||||||
let pn_ts_raw: String = if pmp >= 0 {
|
|
||||||
let ps: Int = pmp + str_len(pm)
|
|
||||||
let pr: String = str_slice(pn_c, ps, str_len(pn_c))
|
|
||||||
let pn_next: Int = str_index_of(pr, " | ")
|
|
||||||
if pn_next < 0 { pr } else { str_slice(pr, 0, pn_next) }
|
|
||||||
} else {
|
|
||||||
let pca: String = json_get(pn, "created_at")
|
|
||||||
if str_eq(pca, "") { json_get(pn, "updated_at") } else { pca }
|
|
||||||
}
|
|
||||||
let pn_ts: Int = if str_eq(pn_ts_raw, "") { 0 } else { str_to_int(pn_ts_raw) }
|
|
||||||
let psnip: String = if str_len(pn_c) > 200 { str_slice(pn_c, 0, 200) } else { pn_c }
|
|
||||||
let pacc = if pn_ts >= aff_7d && !str_eq(psnip, "") {
|
|
||||||
if str_eq(pacc, "") { psnip } else { pacc + "\n" + psnip }
|
|
||||||
} else { pacc }
|
|
||||||
let pi = pi + 1
|
|
||||||
pacc
|
|
||||||
}
|
|
||||||
pacc
|
|
||||||
} else { aff_ctx }
|
} else { aff_ctx }
|
||||||
if !str_eq(aff_ctx, "") {
|
if !str_eq(aff_ctx, "") {
|
||||||
state_set("soul_affective_context", aff_ctx)
|
state_set("soul_affective_context", aff_ctx)
|
||||||
|
|||||||
Executable
+221
@@ -0,0 +1,221 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# cultivation-digest.sh — Neuron daily cultivation digest
|
||||||
|
# Reads ~/.neuron/engram/snapshot.json and produces a sharpness report.
|
||||||
|
# Writes to ~/.neuron/digests/YYYY-MM-DD.txt and appends to sharpness.json.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SNAPSHOT="$HOME/.neuron/engram/snapshot.json"
|
||||||
|
DIGESTS_DIR="$HOME/.neuron/digests"
|
||||||
|
DATE=$(date +%Y-%m-%d)
|
||||||
|
DIGEST_FILE="$DIGESTS_DIR/$DATE.txt"
|
||||||
|
SHARPNESS_FILE="$DIGESTS_DIR/sharpness.json"
|
||||||
|
|
||||||
|
mkdir -p "$DIGESTS_DIR"
|
||||||
|
|
||||||
|
if [[ ! -f "$SNAPSHOT" ]]; then
|
||||||
|
echo "ERROR: snapshot not found at $SNAPSHOT" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cutoff: now minus 24 hours in milliseconds
|
||||||
|
NOW_MS=$(( $(date +%s) * 1000 ))
|
||||||
|
CUTOFF_MS=$(( NOW_MS - 86400000 ))
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Compute all metrics via a single jq pass (avoids re-reading 174 MB 10x)
|
||||||
|
# Fields in item lines are tab-separated: type TAB importance TAB content
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
METRICS=$(jq -r --argjson cutoff "$CUTOFF_MS" '
|
||||||
|
.nodes as $all |
|
||||||
|
|
||||||
|
# Real memory nodes — exclude InternalStateEvent and corrupted entries
|
||||||
|
($all | map(select(
|
||||||
|
.node_type != "InternalStateEvent" and
|
||||||
|
(.node_type | test("^[A-Za-z]+$"))
|
||||||
|
))) as $real |
|
||||||
|
|
||||||
|
# Created today
|
||||||
|
($real | map(select(.created_at > $cutoff))) as $new |
|
||||||
|
|
||||||
|
# Activated today but not created today (reinforced)
|
||||||
|
($real | map(select(
|
||||||
|
(.last_activated // 0) > $cutoff and
|
||||||
|
.created_at <= $cutoff
|
||||||
|
))) as $reinforced |
|
||||||
|
|
||||||
|
# Stats for sharpness (across all real nodes)
|
||||||
|
($real | length) as $real_count |
|
||||||
|
($real | if length > 0 then (map(.importance) | add / length) else 0 end) as $avg_imp |
|
||||||
|
($real | if length > 0 then (map(.confidence // 1) | add / length) else 0 end) as $avg_conf |
|
||||||
|
|
||||||
|
# activation_ratio: reinforced nodes today / total real nodes, capped 0-1
|
||||||
|
(($reinforced | length) as $ra |
|
||||||
|
if $real_count > 0 then ($ra / $real_count | if . > 1 then 1 else . end) else 0 end
|
||||||
|
) as $act_ratio |
|
||||||
|
|
||||||
|
# Sharpness score 0-100
|
||||||
|
((($avg_imp * 0.4) + ($avg_conf * 0.3) + ($act_ratio * 0.3)) * 100 | round) as $sharpness |
|
||||||
|
|
||||||
|
# Top new memories (by importance desc, cap 10)
|
||||||
|
($new | sort_by(-.importance) | .[0:10]) as $top_new |
|
||||||
|
|
||||||
|
# Top reinforced (by last_activated desc, cap 10)
|
||||||
|
($reinforced | sort_by(-.last_activated) | .[0:10]) as $top_reinforced |
|
||||||
|
|
||||||
|
# High-importance nodes (importance > 0.8), across all real nodes
|
||||||
|
($real | map(select(.importance > 0.8)) | length) as $high_imp_count |
|
||||||
|
|
||||||
|
# Scalar metrics
|
||||||
|
"TOTAL_REAL=\($real_count)",
|
||||||
|
"NEW_COUNT=\($new | length)",
|
||||||
|
"REINFORCED_COUNT=\($reinforced | length)",
|
||||||
|
"TOTAL_NODES=\($all | length)",
|
||||||
|
"AVG_IMP=\($avg_imp)",
|
||||||
|
"AVG_CONF=\($avg_conf)",
|
||||||
|
"ACT_RATIO=\($act_ratio)",
|
||||||
|
"SHARPNESS=\($sharpness)",
|
||||||
|
"HIGH_IMP=\($high_imp_count)",
|
||||||
|
|
||||||
|
# Item sections — fields separated by tab character (\t)
|
||||||
|
"---NEW---",
|
||||||
|
($top_new[] | [.node_type, (.importance | tostring), (.content[0:120] | gsub("\n";" "))] | join("\t")),
|
||||||
|
"---REINFORCED---",
|
||||||
|
($top_reinforced[] | [(.label[0:80] | gsub("\n";" ")), ("activated \(.activation_count)x total")] | join("\t"))
|
||||||
|
' "$SNAPSHOT" 2>/dev/null)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Parse scalar metrics
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
parse() { printf '%s' "$METRICS" | grep "^$1=" | head -1 | cut -d= -f2-; }
|
||||||
|
|
||||||
|
TOTAL_REAL=$(parse TOTAL_REAL)
|
||||||
|
NEW_COUNT=$(parse NEW_COUNT)
|
||||||
|
REINFORCED_COUNT=$(parse REINFORCED_COUNT)
|
||||||
|
TOTAL_NODES=$(parse TOTAL_NODES)
|
||||||
|
AVG_IMP=$(parse AVG_IMP)
|
||||||
|
AVG_CONF=$(parse AVG_CONF)
|
||||||
|
ACT_RATIO=$(parse ACT_RATIO)
|
||||||
|
SHARPNESS=$(parse SHARPNESS)
|
||||||
|
HIGH_IMP=$(parse HIGH_IMP)
|
||||||
|
|
||||||
|
# Format floats to 2dp (use awk, avoiding bc locale issues)
|
||||||
|
fmt2() { awk "BEGIN{printf \"%.2f\", $1}"; }
|
||||||
|
fmt4() { awk "BEGIN{printf \"%.4f\", $1}"; }
|
||||||
|
AVG_IMP_FMT=$(fmt2 "$AVG_IMP")
|
||||||
|
AVG_CONF_FMT=$(fmt2 "$AVG_CONF")
|
||||||
|
ACT_RATIO_FMT=$(fmt4 "$ACT_RATIO")
|
||||||
|
IMP_CONTRIB=$(fmt4 "$(awk "BEGIN{printf \"%.6f\", $AVG_IMP * 0.4}")")
|
||||||
|
CONF_CONTRIB=$(fmt4 "$(awk "BEGIN{printf \"%.6f\", $AVG_CONF * 0.3}")")
|
||||||
|
ACT_CONTRIB=$(fmt4 "$(awk "BEGIN{printf \"%.6f\", $ACT_RATIO * 0.3}")")
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Sharpness delta (compare to yesterday)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
DELTA_STR=""
|
||||||
|
if [[ -f "$SHARPNESS_FILE" ]]; then
|
||||||
|
YESTERDAY=$(date -v-1d +%Y-%m-%d 2>/dev/null || date -d "yesterday" +%Y-%m-%d 2>/dev/null || echo "")
|
||||||
|
if [[ -n "$YESTERDAY" ]]; then
|
||||||
|
PREV_SHARPNESS=$(jq -r --arg d "$YESTERDAY" '.[] | select(.date == $d) | .sharpness' "$SHARPNESS_FILE" 2>/dev/null | tail -1)
|
||||||
|
if [[ -n "$PREV_SHARPNESS" && "$PREV_SHARPNESS" != "null" ]]; then
|
||||||
|
DELTA=$(( SHARPNESS - PREV_SHARPNESS ))
|
||||||
|
if (( DELTA > 0 )); then
|
||||||
|
DELTA_STR=" (up ${DELTA}% from yesterday)"
|
||||||
|
elif (( DELTA < 0 )); then
|
||||||
|
DELTA_STR=" (down ${DELTA#-}% from yesterday)"
|
||||||
|
else
|
||||||
|
DELTA_STR=" (no change from yesterday)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Build new-memories section (tab-delimited: type TAB importance TAB content)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
new_section() {
|
||||||
|
local lines
|
||||||
|
lines=$(printf '%s\n' "$METRICS" | awk '/^---NEW---/{found=1; next} /^---REINFORCED---/{exit} found{print}')
|
||||||
|
if [[ -z "$lines" ]]; then
|
||||||
|
echo " (none)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
while IFS=$'\t' read -r ntype importance content; do
|
||||||
|
[[ -z "$ntype" ]] && continue
|
||||||
|
imp_fmt=$(awk "BEGIN{printf \"%.1f\", $importance}")
|
||||||
|
printf " [%-18s] (importance: %s) %s\n" "$ntype" "$imp_fmt" "$content"
|
||||||
|
done <<< "$lines"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Build reinforced section (tab-delimited: label TAB activation-info)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
reinforced_section() {
|
||||||
|
local lines
|
||||||
|
lines=$(printf '%s\n' "$METRICS" | awk '/^---REINFORCED---/{found=1; next} found{print}')
|
||||||
|
if [[ -z "$lines" ]]; then
|
||||||
|
echo " (none today)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
while IFS=$'\t' read -r label acts; do
|
||||||
|
[[ -z "$label" ]] && continue
|
||||||
|
printf " \"%s\" — %s\n" "$label" "$acts"
|
||||||
|
done <<< "$lines"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Render full digest
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
DIGEST=$(cat <<EOF
|
||||||
|
=== Neuron Cultivation Digest — ${DATE} ===
|
||||||
|
|
||||||
|
SHARPNESS: ${SHARPNESS}%${DELTA_STR}
|
||||||
|
|
||||||
|
TODAY'S MEMORIES (${NEW_COUNT} new):
|
||||||
|
$(new_section)
|
||||||
|
|
||||||
|
REINFORCED (${REINFORCED_COUNT} nodes re-activated today):
|
||||||
|
$(reinforced_section)
|
||||||
|
|
||||||
|
MEMORY HEALTH:
|
||||||
|
Total nodes (all): ${TOTAL_NODES}
|
||||||
|
Real memory nodes: ${TOTAL_REAL}
|
||||||
|
Avg importance: ${AVG_IMP_FMT}
|
||||||
|
Avg confidence: ${AVG_CONF_FMT}
|
||||||
|
High-importance nodes (>0.8): ${HIGH_IMP}
|
||||||
|
Nodes created today: ${NEW_COUNT}
|
||||||
|
Nodes re-activated today: ${REINFORCED_COUNT}
|
||||||
|
|
||||||
|
SHARPNESS FORMULA:
|
||||||
|
Sharpness = (avg_importance x 0.4) + (avg_confidence x 0.3) + (activation_ratio x 0.3)
|
||||||
|
avg_importance = ${AVG_IMP_FMT} -> ${AVG_IMP_FMT} x 0.4 = ${IMP_CONTRIB}
|
||||||
|
avg_confidence = ${AVG_CONF_FMT} -> ${AVG_CONF_FMT} x 0.3 = ${CONF_CONTRIB}
|
||||||
|
activation_ratio = ${ACT_RATIO_FMT} -> ratio x 0.3 = ${ACT_CONTRIB}
|
||||||
|
Result: ${SHARPNESS}%
|
||||||
|
|
||||||
|
Generated: $(date)
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Write digest file + print to stdout
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
printf '%s\n' "$DIGEST" | tee "$DIGEST_FILE"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Append to sharpness.json
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
NEW_ENTRY="{\"date\":\"${DATE}\",\"sharpness\":${SHARPNESS},\"node_count\":${TOTAL_NODES},\"real_node_count\":${TOTAL_REAL},\"nodes_added\":${NEW_COUNT},\"nodes_reinforced\":${REINFORCED_COUNT}}"
|
||||||
|
|
||||||
|
if [[ -f "$SHARPNESS_FILE" ]]; then
|
||||||
|
UPDATED=$(jq --arg d "$DATE" --argjson entry "$NEW_ENTRY" '
|
||||||
|
map(select(.date != $d)) + [$entry]
|
||||||
|
' "$SHARPNESS_FILE" 2>/dev/null) || UPDATED="[$NEW_ENTRY]"
|
||||||
|
printf '%s\n' "$UPDATED" > "$SHARPNESS_FILE"
|
||||||
|
else
|
||||||
|
printf '[%s]\n' "$NEW_ENTRY" > "$SHARPNESS_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Digest written to: $DIGEST_FILE"
|
||||||
|
echo "Sharpness log: $SHARPNESS_FILE"
|
||||||
Executable
+162
@@ -0,0 +1,162 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# memory-export.sh — Export Neuron engram store as a portable encrypted .neuronmem bundle
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./tools/memory-export.sh [output-path] [--passphrase "your passphrase"]
|
||||||
|
#
|
||||||
|
# If no passphrase is given, a random one is generated and printed — write it down.
|
||||||
|
# If no output path is given, defaults to ./neuron-export-<timestamp>.neuronmem
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ── Config ─────────────────────────────────────────────────────────────────────
|
||||||
|
ENGRAM_SNAPSHOT="${HOME}/.neuron/engram/snapshot.json"
|
||||||
|
SOUL_VERSION="1.1.0"
|
||||||
|
FORMAT_VERSION="1"
|
||||||
|
|
||||||
|
# ── Parse args ─────────────────────────────────────────────────────────────────
|
||||||
|
OUTPUT_PATH=""
|
||||||
|
PASSPHRASE=""
|
||||||
|
PASSPHRASE_SET=0
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--passphrase)
|
||||||
|
PASSPHRASE="$2"
|
||||||
|
PASSPHRASE_SET=1
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--passphrase=*)
|
||||||
|
PASSPHRASE="${1#*=}"
|
||||||
|
PASSPHRASE_SET=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-*)
|
||||||
|
echo "Unknown option: $1" >&2
|
||||||
|
echo "Usage: $0 [output-path] [--passphrase \"...\"]" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if [[ -z "$OUTPUT_PATH" ]]; then
|
||||||
|
OUTPUT_PATH="$1"
|
||||||
|
else
|
||||||
|
echo "Unexpected argument: $1" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── Default output path ────────────────────────────────────────────────────────
|
||||||
|
TIMESTAMP="$(date -u +"%Y%m%dT%H%M%SZ")"
|
||||||
|
if [[ -z "$OUTPUT_PATH" ]]; then
|
||||||
|
OUTPUT_PATH="./neuron-export-${TIMESTAMP}.neuronmem"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure .neuronmem extension
|
||||||
|
if [[ "${OUTPUT_PATH}" != *.neuronmem ]]; then
|
||||||
|
OUTPUT_PATH="${OUTPUT_PATH%.neuronmem}.neuronmem"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Validate source ────────────────────────────────────────────────────────────
|
||||||
|
if [[ ! -f "$ENGRAM_SNAPSHOT" ]]; then
|
||||||
|
echo "ERROR: Engram snapshot not found at: $ENGRAM_SNAPSHOT" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Neuron Memory Export"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "Source: $ENGRAM_SNAPSHOT"
|
||||||
|
echo "Output: $OUTPUT_PATH"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── Generate passphrase if not provided ────────────────────────────────────────
|
||||||
|
if [[ $PASSPHRASE_SET -eq 0 ]]; then
|
||||||
|
PASSPHRASE="$(openssl rand -base64 32)"
|
||||||
|
echo "⚠ No passphrase provided. Generated passphrase:"
|
||||||
|
echo ""
|
||||||
|
echo " ${PASSPHRASE}"
|
||||||
|
echo ""
|
||||||
|
echo "⚠ WRITE THIS DOWN. You will need it to import this file."
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Count nodes and edges ──────────────────────────────────────────────────────
|
||||||
|
echo "Analyzing snapshot..."
|
||||||
|
NODE_COUNT="$(python3 -c "
|
||||||
|
import json, sys
|
||||||
|
with open('${ENGRAM_SNAPSHOT}') as f:
|
||||||
|
d = json.load(f)
|
||||||
|
nodes = d.get('nodes', d if isinstance(d, list) else [])
|
||||||
|
edges = d.get('edges', [])
|
||||||
|
print(len(nodes) if isinstance(nodes, list) else len(nodes))
|
||||||
|
" 2>/dev/null || echo "unknown")"
|
||||||
|
|
||||||
|
echo " Nodes: ${NODE_COUNT}"
|
||||||
|
|
||||||
|
# ── Compute checksum of source file ───────────────────────────────────────────
|
||||||
|
echo "Computing checksum..."
|
||||||
|
CHECKSUM="$(openssl dgst -sha256 "$ENGRAM_SNAPSHOT" | awk '{print $NF}')"
|
||||||
|
echo " SHA256: ${CHECKSUM:0:16}..."
|
||||||
|
|
||||||
|
# ── Build bundle in temp dir ───────────────────────────────────────────────────
|
||||||
|
WORK_DIR="$(mktemp -d)"
|
||||||
|
BUNDLE_DIR="${WORK_DIR}/neuronmem-v${FORMAT_VERSION}"
|
||||||
|
mkdir -p "$BUNDLE_DIR"
|
||||||
|
|
||||||
|
echo "Building bundle..."
|
||||||
|
|
||||||
|
# Copy snapshot as nodes.json
|
||||||
|
cp "$ENGRAM_SNAPSHOT" "${BUNDLE_DIR}/nodes.json"
|
||||||
|
|
||||||
|
# Write metadata.json
|
||||||
|
ISO_TIMESTAMP="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
||||||
|
cat > "${BUNDLE_DIR}/metadata.json" << METAEOF
|
||||||
|
{
|
||||||
|
"version": "${FORMAT_VERSION}",
|
||||||
|
"exported_at": "${ISO_TIMESTAMP}",
|
||||||
|
"node_count": ${NODE_COUNT},
|
||||||
|
"soul_version": "${SOUL_VERSION}",
|
||||||
|
"sha256": "${CHECKSUM}",
|
||||||
|
"format": "neuronmem-v1",
|
||||||
|
"encryption": "aes-256-cbc-pbkdf2",
|
||||||
|
"source_host": "$(hostname -s 2>/dev/null || echo unknown)"
|
||||||
|
}
|
||||||
|
METAEOF
|
||||||
|
|
||||||
|
echo " metadata.json written"
|
||||||
|
echo " nodes.json copied ($(du -sh "${BUNDLE_DIR}/nodes.json" | cut -f1))"
|
||||||
|
|
||||||
|
# ── Create tar.gz ──────────────────────────────────────────────────────────────
|
||||||
|
TAR_PATH="${WORK_DIR}/bundle.tar.gz"
|
||||||
|
echo "Compressing..."
|
||||||
|
(cd "$WORK_DIR" && tar czf "$TAR_PATH" "neuronmem-v${FORMAT_VERSION}/")
|
||||||
|
COMPRESSED_SIZE="$(du -sh "$TAR_PATH" | cut -f1)"
|
||||||
|
echo " Compressed size: ${COMPRESSED_SIZE}"
|
||||||
|
|
||||||
|
# ── Encrypt ────────────────────────────────────────────────────────────────────
|
||||||
|
echo "Encrypting (AES-256-CBC, PBKDF2, 600k iterations)..."
|
||||||
|
openssl enc -aes-256-cbc \
|
||||||
|
-pbkdf2 \
|
||||||
|
-iter 600000 \
|
||||||
|
-salt \
|
||||||
|
-in "$TAR_PATH" \
|
||||||
|
-out "$OUTPUT_PATH" \
|
||||||
|
-pass "pass:${PASSPHRASE}"
|
||||||
|
|
||||||
|
# ── Cleanup ────────────────────────────────────────────────────────────────────
|
||||||
|
rm -rf "$WORK_DIR"
|
||||||
|
|
||||||
|
# ── Report ─────────────────────────────────────────────────────────────────────
|
||||||
|
FINAL_SIZE="$(du -sh "$OUTPUT_PATH" | cut -f1)"
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "Export complete."
|
||||||
|
echo " File: $OUTPUT_PATH"
|
||||||
|
echo " Size: ${FINAL_SIZE}"
|
||||||
|
echo " Nodes: ${NODE_COUNT}"
|
||||||
|
echo " Checksum: ${CHECKSUM:0:32}..."
|
||||||
|
echo " Timestamp: ${ISO_TIMESTAMP}"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
Executable
+427
@@ -0,0 +1,427 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# memory-import-refugee.sh — Import conversation/memory history from external apps into Neuron
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./tools/memory-import-refugee.sh --format chatgpt conversations.json
|
||||||
|
# ./tools/memory-import-refugee.sh --format screenpipe screenpipe-export.json
|
||||||
|
# ./tools/memory-import-refugee.sh --format generic data.json[l]
|
||||||
|
#
|
||||||
|
# Supported formats:
|
||||||
|
# chatgpt — ChatGPT conversation export (conversations.json)
|
||||||
|
# screenpipe — Screenpipe OCR export (frames array)
|
||||||
|
# generic — Any JSON array or JSONL with content/text fields
|
||||||
|
#
|
||||||
|
# The script writes Memory nodes to the Neuron soul via its HTTP API.
|
||||||
|
# The soul must be running on localhost:7770.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ── Config ─────────────────────────────────────────────────────────────────────
|
||||||
|
SOUL_HOST="http://localhost:7770"
|
||||||
|
# Note: POST /api/neuron/memory ignores the label field (soul hardcodes "memory:remembered").
|
||||||
|
# We embed the label in the content prefix so it is searchable.
|
||||||
|
MEMORY_API="${SOUL_HOST}/api/neuron/memory"
|
||||||
|
SLEEP_MS=100 # ms between API calls (rate limiting)
|
||||||
|
|
||||||
|
# ── Dependency check ───────────────────────────────────────────────────────────
|
||||||
|
if ! command -v jq &>/dev/null; then
|
||||||
|
echo "ERROR: jq is required but not installed." >&2
|
||||||
|
echo "" >&2
|
||||||
|
echo "Install it with:" >&2
|
||||||
|
echo " macOS: brew install jq" >&2
|
||||||
|
echo " Ubuntu: sudo apt-get install jq" >&2
|
||||||
|
echo " Alpine: apk add jq" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Parse args ─────────────────────────────────────────────────────────────────
|
||||||
|
FORMAT=""
|
||||||
|
INPUT_FILE=""
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--format|-f)
|
||||||
|
FORMAT="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--format=*|-f=*)
|
||||||
|
FORMAT="${1#*=}"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-*)
|
||||||
|
echo "Unknown option: $1" >&2
|
||||||
|
echo "Usage: $0 --format <chatgpt|screenpipe|generic> <input-file>" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if [[ -z "$INPUT_FILE" ]]; then
|
||||||
|
INPUT_FILE="$1"
|
||||||
|
else
|
||||||
|
echo "Unexpected argument: $1" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z "$FORMAT" ]]; then
|
||||||
|
echo "ERROR: --format is required." >&2
|
||||||
|
echo "Usage: $0 --format <chatgpt|screenpipe|generic> <input-file>" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$INPUT_FILE" ]]; then
|
||||||
|
echo "ERROR: No input file specified." >&2
|
||||||
|
echo "Usage: $0 --format <chatgpt|screenpipe|generic> <input-file>" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f "$INPUT_FILE" ]]; then
|
||||||
|
echo "ERROR: Input file not found: $INPUT_FILE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$FORMAT" in
|
||||||
|
chatgpt|screenpipe|generic) ;;
|
||||||
|
*)
|
||||||
|
echo "ERROR: Unknown format: $FORMAT" >&2
|
||||||
|
echo "Supported formats: chatgpt, screenpipe, generic" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# ── Soul health check ──────────────────────────────────────────────────────────
|
||||||
|
HTTP_CODE="$(curl -s -o /dev/null -w "%{http_code}" "${SOUL_HOST}/api/neuron/memory" 2>/dev/null || echo "000")"
|
||||||
|
if [[ "$HTTP_CODE" == "000" ]]; then
|
||||||
|
echo "ERROR: Neuron soul is not responding at ${SOUL_HOST}." >&2
|
||||||
|
echo " Start the soul service and retry." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Counters ───────────────────────────────────────────────────────────────────
|
||||||
|
IMPORTED=0
|
||||||
|
SKIPPED=0
|
||||||
|
ERRORS=0
|
||||||
|
|
||||||
|
# ── Helper: post one memory node ───────────────────────────────────────────────
|
||||||
|
# post_memory CONTENT LABEL TAGS_JSON
|
||||||
|
#
|
||||||
|
# Note: the soul's POST /api/neuron/memory API ignores the label field (hardcodes
|
||||||
|
# it to "memory:remembered"). We embed the label as a prefix in the content so
|
||||||
|
# the title remains searchable via recall/search.
|
||||||
|
post_memory() {
|
||||||
|
local content="$1"
|
||||||
|
local label="$2"
|
||||||
|
local tags_json="$3"
|
||||||
|
|
||||||
|
# Skip empty content
|
||||||
|
if [[ -z "$content" || "$content" == "null" ]]; then
|
||||||
|
SKIPPED=$((SKIPPED + 1))
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Embed label in content so it's searchable (the API ignores the label field)
|
||||||
|
local full_content="[${label}] ${content}"
|
||||||
|
|
||||||
|
local payload
|
||||||
|
payload="$(jq -n \
|
||||||
|
--arg content "$full_content" \
|
||||||
|
--arg label "$label" \
|
||||||
|
--argjson tags "$tags_json" \
|
||||||
|
'{content: $content, label: $label, tags: $tags}')"
|
||||||
|
|
||||||
|
local response
|
||||||
|
response="$(curl -s -X POST "$MEMORY_API" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "$payload" 2>/dev/null)"
|
||||||
|
|
||||||
|
local ok
|
||||||
|
ok="$(echo "$response" | jq -r '.ok // "false"' 2>/dev/null)"
|
||||||
|
|
||||||
|
if [[ "$ok" == "true" ]]; then
|
||||||
|
IMPORTED=$((IMPORTED + 1))
|
||||||
|
else
|
||||||
|
ERRORS=$((ERRORS + 1))
|
||||||
|
echo " [ERROR] API error for label \"${label:0:60}\": $response" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Rate limit: sleep 100ms
|
||||||
|
sleep "0.${SLEEP_MS}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Format: ChatGPT ────────────────────────────────────────────────────────────
|
||||||
|
import_chatgpt() {
|
||||||
|
echo "Format: ChatGPT conversation export"
|
||||||
|
|
||||||
|
# Validate: must be JSON array at top level
|
||||||
|
local top_type
|
||||||
|
top_type="$(jq -r 'type' "$INPUT_FILE" 2>/dev/null)"
|
||||||
|
if [[ "$top_type" != "array" ]]; then
|
||||||
|
echo "ERROR: ChatGPT export must be a JSON array of conversations." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local conv_count
|
||||||
|
conv_count="$(jq 'length' "$INPUT_FILE")"
|
||||||
|
echo "Found ${conv_count} conversation(s) to process."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Count total user messages for progress display
|
||||||
|
local total_msgs
|
||||||
|
total_msgs="$(jq '[.[].mapping // {} | to_entries[] | .value.message | select(. != null and .author.role == "user") | .content.parts // [] | .[] | select(type == "string" and length > 0)] | length' "$INPUT_FILE" 2>/dev/null || echo "?")"
|
||||||
|
echo "Total user messages: ${total_msgs}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
local msg_idx=0
|
||||||
|
|
||||||
|
# Process each conversation
|
||||||
|
while IFS= read -r conv_json; do
|
||||||
|
local title
|
||||||
|
title="$(echo "$conv_json" | jq -r '.title // "Untitled"')"
|
||||||
|
|
||||||
|
# Truncate label to 100 chars
|
||||||
|
local label="${title:0:100}"
|
||||||
|
|
||||||
|
# Extract user messages — ChatGPT export uses a mapping dict structure
|
||||||
|
# Mapping: { uuid: { id, message: { author: { role }, content: { parts: [...] } }, ... } }
|
||||||
|
# We iterate over mapping values, filter role=user, grab text parts
|
||||||
|
while IFS= read -r msg_text; do
|
||||||
|
msg_idx=$((msg_idx + 1))
|
||||||
|
echo " Importing ${msg_idx}/${total_msgs}..."
|
||||||
|
post_memory "$msg_text" "$label" '["chatgpt-import","conversation"]'
|
||||||
|
done < <(echo "$conv_json" | jq -r '
|
||||||
|
.mapping // {} |
|
||||||
|
to_entries[] |
|
||||||
|
.value.message |
|
||||||
|
select(. != null) |
|
||||||
|
select(.author.role == "user") |
|
||||||
|
.content.parts // [] |
|
||||||
|
.[] |
|
||||||
|
select(type == "string" and length > 0)
|
||||||
|
' 2>/dev/null)
|
||||||
|
|
||||||
|
done < <(jq -c '.[]' "$INPUT_FILE")
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Format: Screenpipe ─────────────────────────────────────────────────────────
|
||||||
|
import_screenpipe() {
|
||||||
|
echo "Format: Screenpipe OCR export"
|
||||||
|
|
||||||
|
# Validate: must have frames array
|
||||||
|
local top_type
|
||||||
|
top_type="$(jq -r 'type' "$INPUT_FILE" 2>/dev/null)"
|
||||||
|
if [[ "$top_type" != "object" ]]; then
|
||||||
|
echo "ERROR: Screenpipe export must be a JSON object with a 'frames' array." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local frame_count
|
||||||
|
frame_count="$(jq '.frames | length' "$INPUT_FILE" 2>/dev/null || echo "0")"
|
||||||
|
echo "Found ${frame_count} frame(s) to process."
|
||||||
|
|
||||||
|
if [[ "$frame_count" == "0" ]]; then
|
||||||
|
echo "No frames found. Nothing to import."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Group frames by app_name + 5-minute window bucket
|
||||||
|
# Strategy: process sorted frames, emit a group when app or bucket changes.
|
||||||
|
# We do this in pure jq with a reduce, emitting groups as newline-delimited JSON.
|
||||||
|
|
||||||
|
local total_groups=0
|
||||||
|
local group_idx=0
|
||||||
|
|
||||||
|
# Collect groups: each group is { app, bucket_ts, texts: [...] }
|
||||||
|
# Bucket = floor(timestamp_epoch / 300) * 300 seconds
|
||||||
|
# timestamps may be ISO8601 or epoch — handle both
|
||||||
|
|
||||||
|
# We process in jq and emit one group per line as JSON
|
||||||
|
while IFS= read -r group_json; do
|
||||||
|
total_groups=$((total_groups + 1))
|
||||||
|
# Just count first
|
||||||
|
:
|
||||||
|
done < <(jq -c '
|
||||||
|
.frames |
|
||||||
|
map(select(.text != null and (.text | length) > 0)) |
|
||||||
|
group_by(.app_name) |
|
||||||
|
.[] |
|
||||||
|
. as $app_frames |
|
||||||
|
($app_frames[0].app_name) as $app |
|
||||||
|
# Sort by timestamp within app
|
||||||
|
(sort_by(.timestamp)) |
|
||||||
|
# Group into 5-minute buckets
|
||||||
|
reduce .[] as $f (
|
||||||
|
{bucket: null, texts: [], ts: null, groups: []};
|
||||||
|
($f.timestamp // "") as $ts |
|
||||||
|
# Derive numeric bucket: try epoch directly; for ISO use first 15 chars as bucket key
|
||||||
|
(if ($ts | test("^[0-9]+$")) then ($ts | tonumber / 300 | floor)
|
||||||
|
else ($ts[0:15])
|
||||||
|
end) as $bucket |
|
||||||
|
if .bucket == null then
|
||||||
|
{bucket: $bucket, texts: [$f.text], ts: $ts, groups: .groups}
|
||||||
|
elif .bucket == $bucket then
|
||||||
|
{bucket: $bucket, texts: (.texts + [$f.text]), ts: $ts, groups: .groups}
|
||||||
|
else
|
||||||
|
{bucket: $bucket, texts: [$f.text], ts: $ts,
|
||||||
|
groups: (.groups + [{app: $app, ts: .ts, texts: .texts}])}
|
||||||
|
end
|
||||||
|
) |
|
||||||
|
# flush last bucket
|
||||||
|
(.groups + [{app: .app_name, ts: .ts, texts: .texts}]) |
|
||||||
|
.[] |
|
||||||
|
select(.texts | length > 0)
|
||||||
|
' "$INPUT_FILE" 2>/dev/null)
|
||||||
|
|
||||||
|
# Now actually process
|
||||||
|
while IFS= read -r group_json; do
|
||||||
|
group_idx=$((group_idx + 1))
|
||||||
|
echo " Importing ${group_idx}..."
|
||||||
|
|
||||||
|
local app_name ts_str content label
|
||||||
|
|
||||||
|
app_name="$(echo "$group_json" | jq -r '.app // "unknown"')"
|
||||||
|
ts_str="$(echo "$group_json" | jq -r '.ts // ""')"
|
||||||
|
|
||||||
|
# Concatenate texts, truncate to 2000 chars
|
||||||
|
content="$(echo "$group_json" | jq -r '.texts | join(" ")' | cut -c1-2000)"
|
||||||
|
label="Screenpipe: ${app_name} at ${ts_str:0:16}"
|
||||||
|
|
||||||
|
local tags_json
|
||||||
|
tags_json="$(jq -n --arg app "$app_name" '["screenpipe-import","screen-capture",$app]')"
|
||||||
|
|
||||||
|
post_memory "$content" "$label" "$tags_json"
|
||||||
|
|
||||||
|
done < <(jq -c '
|
||||||
|
.frames |
|
||||||
|
map(select(.text != null and (.text | length) > 0)) |
|
||||||
|
group_by(.app_name) |
|
||||||
|
.[] |
|
||||||
|
. as $app_frames |
|
||||||
|
($app_frames[0].app_name) as $app |
|
||||||
|
(sort_by(.timestamp)) |
|
||||||
|
reduce .[] as $f (
|
||||||
|
{bucket: null, texts: [], ts: null, app: $app, groups: []};
|
||||||
|
($f.timestamp // "") as $ts |
|
||||||
|
(if ($ts | test("^[0-9]+$")) then ($ts | tonumber / 300 | floor | tostring)
|
||||||
|
else ($ts[0:15])
|
||||||
|
end) as $bucket |
|
||||||
|
if .bucket == null then
|
||||||
|
{bucket: $bucket, texts: [$f.text], ts: $ts, app: $app, groups: .groups}
|
||||||
|
elif .bucket == $bucket then
|
||||||
|
{bucket: $bucket, texts: (.texts + [$f.text]), ts: $ts, app: $app, groups: .groups}
|
||||||
|
else
|
||||||
|
{bucket: $bucket, texts: [$f.text], ts: $ts, app: $app,
|
||||||
|
groups: (.groups + [{app: $app, ts: .ts, texts: .texts}])}
|
||||||
|
end
|
||||||
|
) |
|
||||||
|
(.groups + [{app: .app, ts: .ts, texts: .texts}]) |
|
||||||
|
.[] |
|
||||||
|
select(.texts | length > 0)
|
||||||
|
' "$INPUT_FILE" 2>/dev/null)
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Format: Generic ────────────────────────────────────────────────────────────
|
||||||
|
import_generic() {
|
||||||
|
echo "Format: Generic JSON/JSONL"
|
||||||
|
|
||||||
|
# Detect if JSONL (one JSON object per line) or single JSON array/object
|
||||||
|
local first_char
|
||||||
|
first_char="$(head -c1 "$INPUT_FILE" 2>/dev/null)"
|
||||||
|
|
||||||
|
local records_file
|
||||||
|
records_file="$(mktemp)"
|
||||||
|
trap 'rm -f "$records_file"' RETURN
|
||||||
|
|
||||||
|
if [[ "$first_char" == "[" ]]; then
|
||||||
|
# JSON array — explode to one object per line
|
||||||
|
jq -c '.[]' "$INPUT_FILE" > "$records_file" 2>/dev/null || true
|
||||||
|
elif [[ "$first_char" == "{" ]]; then
|
||||||
|
# Single object or JSONL — try JSONL first
|
||||||
|
# JSONL: each line is valid JSON
|
||||||
|
# Check if the whole file is one object or multiple lines
|
||||||
|
local line_count
|
||||||
|
line_count="$(wc -l < "$INPUT_FILE" | tr -d ' ')"
|
||||||
|
if [[ "$line_count" -le 1 ]]; then
|
||||||
|
# Single object: wrap in array and explode
|
||||||
|
jq -c '[.] | .[]' "$INPUT_FILE" > "$records_file" 2>/dev/null || true
|
||||||
|
else
|
||||||
|
# Assume JSONL
|
||||||
|
cp "$INPUT_FILE" "$records_file"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Try JSONL anyway
|
||||||
|
cp "$INPUT_FILE" "$records_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local total_records
|
||||||
|
total_records="$(wc -l < "$records_file" | tr -d ' ')"
|
||||||
|
echo "Found ${total_records} record(s) to process."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
local idx=0
|
||||||
|
while IFS= read -r record_json; do
|
||||||
|
[[ -z "$record_json" ]] && continue
|
||||||
|
|
||||||
|
idx=$((idx + 1))
|
||||||
|
echo " Importing ${idx}/${total_records}..."
|
||||||
|
|
||||||
|
# Extract content: prefer 'content', fall back to 'text', then 'body', then 'message'
|
||||||
|
local content
|
||||||
|
content="$(echo "$record_json" | jq -r '
|
||||||
|
if .content != null and (.content | type) == "string" then .content
|
||||||
|
elif .text != null and (.text | type) == "string" then .text
|
||||||
|
elif .body != null and (.body | type) == "string" then .body
|
||||||
|
elif .message != null and (.message | type) == "string" then .message
|
||||||
|
else ""
|
||||||
|
end
|
||||||
|
' 2>/dev/null)"
|
||||||
|
|
||||||
|
[[ -z "$content" || "$content" == "null" ]] && { SKIPPED=$((SKIPPED + 1)); continue; }
|
||||||
|
|
||||||
|
# Extract label: prefer 'title', then 'label', then 'name', then first 80 chars of content
|
||||||
|
local label
|
||||||
|
label="$(echo "$record_json" | jq -r '
|
||||||
|
if .title != null and (.title | type) == "string" then .title
|
||||||
|
elif .label != null and (.label | type) == "string" then .label
|
||||||
|
elif .name != null and (.name | type) == "string" then .name
|
||||||
|
else ""
|
||||||
|
end
|
||||||
|
' 2>/dev/null)"
|
||||||
|
|
||||||
|
if [[ -z "$label" || "$label" == "null" ]]; then
|
||||||
|
label="${content:0:80}"
|
||||||
|
fi
|
||||||
|
label="${label:0:100}"
|
||||||
|
|
||||||
|
post_memory "$content" "$label" '["imported","generic"]'
|
||||||
|
|
||||||
|
done < "$records_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Main ───────────────────────────────────────────────────────────────────────
|
||||||
|
echo "Neuron Refugee Importer"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "Source: $INPUT_FILE"
|
||||||
|
echo "Format: $FORMAT"
|
||||||
|
echo "Soul: $SOUL_HOST"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
case "$FORMAT" in
|
||||||
|
chatgpt) import_chatgpt ;;
|
||||||
|
screenpipe) import_screenpipe ;;
|
||||||
|
generic) import_generic ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# ── Final report ───────────────────────────────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "Import complete."
|
||||||
|
echo " Imported: ${IMPORTED}"
|
||||||
|
echo " Skipped: ${SKIPPED}"
|
||||||
|
echo " Errors: ${ERRORS}"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
|
||||||
|
if [[ $ERRORS -gt 0 ]]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
Executable
+289
@@ -0,0 +1,289 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# memory-import.sh — Import a Neuron .neuronmem bundle onto this device
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./tools/memory-import.sh input.neuronmem [--passphrase "your passphrase"]
|
||||||
|
# ./tools/memory-import.sh input.neuronmem [--dry-run] # verify only, no changes
|
||||||
|
#
|
||||||
|
# The script will:
|
||||||
|
# 1. Decrypt and unpack the .neuronmem file
|
||||||
|
# 2. Validate the checksum and version
|
||||||
|
# 3. Back up the current snapshot.json
|
||||||
|
# 4. Stop the soul service
|
||||||
|
# 5. Replace snapshot.json
|
||||||
|
# 6. Restart the soul service
|
||||||
|
# 7. Verify the soul came back up
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ── Config ─────────────────────────────────────────────────────────────────────
|
||||||
|
ENGRAM_SNAPSHOT="${HOME}/.neuron/engram/snapshot.json"
|
||||||
|
SOUL_SERVICE="ai.neurontechnologies.soul"
|
||||||
|
SOUL_PORT="7770"
|
||||||
|
SOUL_STARTUP_TIMEOUT=30 # seconds to wait for soul to come back
|
||||||
|
|
||||||
|
# ── Parse args ─────────────────────────────────────────────────────────────────
|
||||||
|
INPUT_PATH=""
|
||||||
|
PASSPHRASE=""
|
||||||
|
PASSPHRASE_SET=0
|
||||||
|
DRY_RUN=0
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--passphrase)
|
||||||
|
PASSPHRASE="$2"
|
||||||
|
PASSPHRASE_SET=1
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--passphrase=*)
|
||||||
|
PASSPHRASE="${1#*=}"
|
||||||
|
PASSPHRASE_SET=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--dry-run)
|
||||||
|
DRY_RUN=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-*)
|
||||||
|
echo "Unknown option: $1" >&2
|
||||||
|
echo "Usage: $0 input.neuronmem [--passphrase \"...\"] [--dry-run]" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if [[ -z "$INPUT_PATH" ]]; then
|
||||||
|
INPUT_PATH="$1"
|
||||||
|
else
|
||||||
|
echo "Unexpected argument: $1" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z "$INPUT_PATH" ]]; then
|
||||||
|
echo "ERROR: No input file specified." >&2
|
||||||
|
echo "Usage: $0 input.neuronmem [--passphrase \"...\"] [--dry-run]" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f "$INPUT_PATH" ]]; then
|
||||||
|
echo "ERROR: Input file not found: $INPUT_PATH" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Neuron Memory Import"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "Source: $INPUT_PATH"
|
||||||
|
echo "Target: $ENGRAM_SNAPSHOT"
|
||||||
|
if [[ $DRY_RUN -eq 1 ]]; then
|
||||||
|
echo "Mode: DRY RUN (no changes will be made)"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── Prompt for passphrase if needed ───────────────────────────────────────────
|
||||||
|
if [[ $PASSPHRASE_SET -eq 0 ]]; then
|
||||||
|
read -r -s -p "Enter passphrase: " PASSPHRASE
|
||||||
|
echo ""
|
||||||
|
if [[ -z "$PASSPHRASE" ]]; then
|
||||||
|
echo "ERROR: Passphrase cannot be empty." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Decrypt to temp dir ────────────────────────────────────────────────────────
|
||||||
|
WORK_DIR="$(mktemp -d)"
|
||||||
|
CLEANUP() {
|
||||||
|
rm -rf "$WORK_DIR"
|
||||||
|
}
|
||||||
|
trap CLEANUP EXIT
|
||||||
|
|
||||||
|
TAR_PATH="${WORK_DIR}/bundle.tar.gz"
|
||||||
|
|
||||||
|
echo "Decrypting..."
|
||||||
|
if ! openssl enc -d -aes-256-cbc \
|
||||||
|
-pbkdf2 \
|
||||||
|
-iter 600000 \
|
||||||
|
-in "$INPUT_PATH" \
|
||||||
|
-out "$TAR_PATH" \
|
||||||
|
-pass "pass:${PASSPHRASE}" 2>/dev/null; then
|
||||||
|
echo "ERROR: Decryption failed. Wrong passphrase or corrupted file." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo " Decrypted successfully."
|
||||||
|
|
||||||
|
# ── Unpack ─────────────────────────────────────────────────────────────────────
|
||||||
|
echo "Unpacking..."
|
||||||
|
(cd "$WORK_DIR" && tar xzf "$TAR_PATH") || {
|
||||||
|
echo "ERROR: Failed to unpack bundle. File may be corrupted." >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Locate the bundle directory (neuronmem-v1/)
|
||||||
|
BUNDLE_DIR=""
|
||||||
|
for d in "${WORK_DIR}"/neuronmem-v*/; do
|
||||||
|
if [[ -d "$d" ]]; then
|
||||||
|
BUNDLE_DIR="$d"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z "$BUNDLE_DIR" ]]; then
|
||||||
|
echo "ERROR: Bundle directory not found. Invalid .neuronmem file." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
METADATA_FILE="${BUNDLE_DIR}metadata.json"
|
||||||
|
NODES_FILE="${BUNDLE_DIR}nodes.json"
|
||||||
|
|
||||||
|
if [[ ! -f "$METADATA_FILE" ]]; then
|
||||||
|
echo "ERROR: metadata.json missing from bundle." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f "$NODES_FILE" ]]; then
|
||||||
|
echo "ERROR: nodes.json missing from bundle." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Validate metadata ──────────────────────────────────────────────────────────
|
||||||
|
echo "Validating metadata..."
|
||||||
|
FORMAT_VERSION="$(python3 -c "import json; d=json.load(open('${METADATA_FILE}')); print(d.get('version','?'))")"
|
||||||
|
EXPORTED_AT="$(python3 -c "import json; d=json.load(open('${METADATA_FILE}')); print(d.get('exported_at','?'))")"
|
||||||
|
EXPECTED_COUNT="$(python3 -c "import json; d=json.load(open('${METADATA_FILE}')); print(d.get('node_count','?'))")"
|
||||||
|
STORED_CHECKSUM="$(python3 -c "import json; d=json.load(open('${METADATA_FILE}')); print(d.get('sha256','?'))")"
|
||||||
|
SOURCE_HOST="$(python3 -c "import json; d=json.load(open('${METADATA_FILE}')); print(d.get('source_host','?'))")"
|
||||||
|
|
||||||
|
echo " Format version: ${FORMAT_VERSION}"
|
||||||
|
echo " Exported at: ${EXPORTED_AT}"
|
||||||
|
echo " Source host: ${SOURCE_HOST}"
|
||||||
|
echo " Expected nodes: ${EXPECTED_COUNT}"
|
||||||
|
|
||||||
|
if [[ "$FORMAT_VERSION" != "1" ]]; then
|
||||||
|
echo "ERROR: Unsupported bundle format version: ${FORMAT_VERSION}" >&2
|
||||||
|
echo " This tool supports version 1 only." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Validate checksum ──────────────────────────────────────────────────────────
|
||||||
|
echo "Verifying checksum..."
|
||||||
|
ACTUAL_CHECKSUM="$(openssl dgst -sha256 "$NODES_FILE" | awk '{print $NF}')"
|
||||||
|
|
||||||
|
if [[ "$ACTUAL_CHECKSUM" != "$STORED_CHECKSUM" ]]; then
|
||||||
|
echo "ERROR: Checksum mismatch!" >&2
|
||||||
|
echo " Expected: ${STORED_CHECKSUM}" >&2
|
||||||
|
echo " Got: ${ACTUAL_CHECKSUM}" >&2
|
||||||
|
echo " The bundle may be corrupted." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo " Checksum OK: ${ACTUAL_CHECKSUM:0:16}..."
|
||||||
|
|
||||||
|
# ── Verify node count ──────────────────────────────────────────────────────────
|
||||||
|
echo "Verifying node count..."
|
||||||
|
ACTUAL_COUNT="$(python3 -c "
|
||||||
|
import json
|
||||||
|
with open('${NODES_FILE}') as f:
|
||||||
|
d = json.load(f)
|
||||||
|
nodes = d.get('nodes', d if isinstance(d, list) else [])
|
||||||
|
print(len(nodes) if isinstance(nodes, list) else len(nodes))
|
||||||
|
" 2>/dev/null || echo "unknown")"
|
||||||
|
|
||||||
|
echo " Found ${ACTUAL_COUNT} nodes (expected ${EXPECTED_COUNT})"
|
||||||
|
|
||||||
|
if [[ "$ACTUAL_COUNT" != "$EXPECTED_COUNT" && "$EXPECTED_COUNT" != "unknown" ]]; then
|
||||||
|
echo "WARNING: Node count mismatch (expected ${EXPECTED_COUNT}, found ${ACTUAL_COUNT})." >&2
|
||||||
|
echo " Proceeding anyway — count may differ if nodes were deduplicated." >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Dry run exit ───────────────────────────────────────────────────────────────
|
||||||
|
if [[ $DRY_RUN -eq 1 ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "DRY RUN complete. Bundle is valid."
|
||||||
|
echo " Nodes: ${ACTUAL_COUNT}"
|
||||||
|
echo " Checksum: verified"
|
||||||
|
echo " Run without --dry-run to import."
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Safety confirmation ────────────────────────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo "WARNING: This will replace your current Neuron memory store."
|
||||||
|
echo " Current snapshot: $ENGRAM_SNAPSHOT"
|
||||||
|
echo " A backup will be created before replacing."
|
||||||
|
echo ""
|
||||||
|
read -r -p "Type 'yes' to continue: " CONFIRM
|
||||||
|
if [[ "$CONFIRM" != "yes" ]]; then
|
||||||
|
echo "Aborted."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Backup existing snapshot ───────────────────────────────────────────────────
|
||||||
|
BACKUP_TIMESTAMP="$(date -u +"%Y%m%dT%H%M%SZ")"
|
||||||
|
ENGRAM_DIR="$(dirname "$ENGRAM_SNAPSHOT")"
|
||||||
|
BACKUP_PATH="${HOME}/.neuron/engram-backup-${BACKUP_TIMESTAMP}.tar.gz"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Backing up current snapshot..."
|
||||||
|
if [[ -f "$ENGRAM_SNAPSHOT" ]]; then
|
||||||
|
(cd "$HOME/.neuron" && tar czf "$BACKUP_PATH" "$(basename "$ENGRAM_DIR")/snapshot.json" 2>/dev/null) || \
|
||||||
|
cp "$ENGRAM_SNAPSHOT" "${ENGRAM_SNAPSHOT}.backup-${BACKUP_TIMESTAMP}"
|
||||||
|
echo " Backup: $BACKUP_PATH"
|
||||||
|
else
|
||||||
|
echo " No existing snapshot to back up."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Stop soul service ──────────────────────────────────────────────────────────
|
||||||
|
echo "Stopping soul service (${SOUL_SERVICE})..."
|
||||||
|
launchctl stop "$SOUL_SERVICE" 2>/dev/null || true
|
||||||
|
# Also stop engram service if running
|
||||||
|
launchctl stop "ai.neuron.engram" 2>/dev/null || true
|
||||||
|
sleep 2
|
||||||
|
echo " Soul stopped."
|
||||||
|
|
||||||
|
# ── Replace snapshot.json ──────────────────────────────────────────────────────
|
||||||
|
echo "Installing new snapshot..."
|
||||||
|
cp "$NODES_FILE" "$ENGRAM_SNAPSHOT"
|
||||||
|
echo " snapshot.json replaced ($(du -sh "$ENGRAM_SNAPSHOT" | cut -f1))"
|
||||||
|
|
||||||
|
# ── Restart soul service ───────────────────────────────────────────────────────
|
||||||
|
echo "Restarting soul service..."
|
||||||
|
launchctl start "$SOUL_SERVICE" 2>/dev/null || true
|
||||||
|
launchctl start "ai.neuron.engram" 2>/dev/null || true
|
||||||
|
|
||||||
|
# ── Wait for soul to come up ───────────────────────────────────────────────────
|
||||||
|
echo "Waiting for soul to come up on port ${SOUL_PORT}..."
|
||||||
|
ELAPSED=0
|
||||||
|
SOUL_UP=0
|
||||||
|
while [[ $ELAPSED -lt $SOUL_STARTUP_TIMEOUT ]]; do
|
||||||
|
if curl -sf "http://localhost:${SOUL_PORT}/" > /dev/null 2>&1; then
|
||||||
|
SOUL_UP=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
# Try a known endpoint that returns any response (even 404 means it's up)
|
||||||
|
HTTP_CODE="$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:${SOUL_PORT}/api/neuron/memory" 2>/dev/null || echo "000")"
|
||||||
|
if [[ "$HTTP_CODE" != "000" ]]; then
|
||||||
|
SOUL_UP=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
ELAPSED=$((ELAPSED + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ $SOUL_UP -eq 1 ]]; then
|
||||||
|
echo " Soul is up (responded in ${ELAPSED}s)."
|
||||||
|
else
|
||||||
|
echo " WARNING: Soul did not respond within ${SOUL_STARTUP_TIMEOUT}s."
|
||||||
|
echo " The service may still be starting. Check: launchctl list | grep soul"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Final report ───────────────────────────────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "Import complete."
|
||||||
|
echo " Nodes imported: ${ACTUAL_COUNT}"
|
||||||
|
echo " Exported at: ${EXPORTED_AT}"
|
||||||
|
echo " Source host: ${SOURCE_HOST}"
|
||||||
|
echo " Backup: ${BACKUP_PATH}"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
Executable
+135
@@ -0,0 +1,135 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# photo-to-memory.sh — OCR a document/photo and store the text in Neuron memory
|
||||||
|
#
|
||||||
|
# Uses GLM-OCR (0.9B, MIT) via mlx-vlm on Apple Silicon.
|
||||||
|
# Model auto-downloads ~1.59 GB to ~/.cache/huggingface/ on first run.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./tools/photo-to-memory.sh <image-file> [--dry-run] [--prompt "custom prompt"]
|
||||||
|
#
|
||||||
|
# Prerequisites:
|
||||||
|
# pip install -U mlx-vlm
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# ./tools/photo-to-memory.sh ~/Desktop/receipt.jpg
|
||||||
|
# ./tools/photo-to-memory.sh ~/Documents/contract.png --dry-run
|
||||||
|
# ./tools/photo-to-memory.sh scan.jpg --prompt "Extract all text from this receipt"
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ── Config ─────────────────────────────────────────────────────────────────────
|
||||||
|
SOUL_URL="${SOUL_URL:-http://localhost:7770}"
|
||||||
|
GLM_MODEL="${GLM_MODEL:-mlx-community/GLM-OCR-8bit}"
|
||||||
|
MAX_TOKENS="${MAX_TOKENS:-4096}"
|
||||||
|
DEFAULT_PROMPT="Extract all text from this document. Preserve structure including tables, headers, and lists. Output plain text."
|
||||||
|
|
||||||
|
# ── Colours ────────────────────────────────────────────────────────────────────
|
||||||
|
RED=$'\033[0;31m'; GREEN=$'\033[0;32m'; YELLOW=$'\033[1;33m'
|
||||||
|
CYAN=$'\033[0;36m'; BOLD=$'\033[1m'; RESET=$'\033[0m'
|
||||||
|
|
||||||
|
log() { printf "%s%s%s\n" "$CYAN" "$*" "$RESET"; }
|
||||||
|
ok() { printf "%s✓ %s%s\n" "$GREEN" "$*" "$RESET"; }
|
||||||
|
warn() { printf "%s⚠ %s%s\n" "$YELLOW" "$*" "$RESET"; }
|
||||||
|
die() { printf "%s✗ %s%s\n" "$RED" "$*" "$RESET" >&2; exit 1; }
|
||||||
|
|
||||||
|
# ── Parse args ─────────────────────────────────────────────────────────────────
|
||||||
|
IMAGE_PATH=""
|
||||||
|
DRY_RUN=0
|
||||||
|
CUSTOM_PROMPT=""
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--dry-run) DRY_RUN=1; shift ;;
|
||||||
|
--prompt) CUSTOM_PROMPT="$2"; shift 2 ;;
|
||||||
|
--model) GLM_MODEL="$2"; shift 2 ;;
|
||||||
|
--help|-h)
|
||||||
|
sed -n '2,15p' "$0" | sed 's/^# \{0,1\}//'
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
-*) die "Unknown option: $1" ;;
|
||||||
|
*)
|
||||||
|
[[ -n "$IMAGE_PATH" ]] && die "Only one image file at a time"
|
||||||
|
IMAGE_PATH="$1"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ -z "$IMAGE_PATH" ]] && die "Usage: $0 <image-file> [--dry-run] [--prompt \"...\"]"
|
||||||
|
[[ -f "$IMAGE_PATH" ]] || die "File not found: $IMAGE_PATH"
|
||||||
|
|
||||||
|
PROMPT="${CUSTOM_PROMPT:-$DEFAULT_PROMPT}"
|
||||||
|
FILENAME=$(basename "$IMAGE_PATH")
|
||||||
|
ABS_PATH=$(realpath "$IMAGE_PATH")
|
||||||
|
|
||||||
|
# ── Check runtime ───────────────────────────────────────────────────────────────
|
||||||
|
if ! python3 -c "import mlx_vlm" 2>/dev/null; then
|
||||||
|
warn "mlx-vlm not installed. Installing now..."
|
||||||
|
pip install -q -U mlx-vlm || die "pip install mlx-vlm failed — run manually: pip install -U mlx-vlm"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Run GLM-OCR ─────────────────────────────────────────────────────────────────
|
||||||
|
log "Running GLM-OCR on: $FILENAME"
|
||||||
|
log "Model: $GLM_MODEL"
|
||||||
|
[[ "$DRY_RUN" -eq 1 ]] && warn "Dry-run mode — will not post to Neuron"
|
||||||
|
|
||||||
|
# GLM-OCR output goes to stdout; capture it
|
||||||
|
# First run downloads ~1.59 GB — this is expected and cached thereafter.
|
||||||
|
OCR_TEXT=$(python3 -m mlx_vlm.generate \
|
||||||
|
--model "$GLM_MODEL" \
|
||||||
|
--max-tokens "$MAX_TOKENS" \
|
||||||
|
--temperature 0.0 \
|
||||||
|
--prompt "$PROMPT" \
|
||||||
|
--image "$ABS_PATH" \
|
||||||
|
2>/dev/null) || die "GLM-OCR failed. Check that mlx-vlm is installed and the image is readable."
|
||||||
|
|
||||||
|
CHAR_COUNT=${#OCR_TEXT}
|
||||||
|
log "OCR complete — extracted ${CHAR_COUNT} characters"
|
||||||
|
|
||||||
|
if [[ "$CHAR_COUNT" -lt 5 ]]; then
|
||||||
|
warn "Very short output — the image may be blank or unreadable"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Preview ─────────────────────────────────────────────────────────────────────
|
||||||
|
printf "\n%s--- OCR output preview (first 400 chars) ---%s\n" "$BOLD" "$RESET"
|
||||||
|
printf "%s\n" "${OCR_TEXT:0:400}"
|
||||||
|
[[ "$CHAR_COUNT" -gt 400 ]] && printf "%s... [+%d more chars]%s\n" "$YELLOW" $((CHAR_COUNT - 400)) "$RESET"
|
||||||
|
printf "\n"
|
||||||
|
|
||||||
|
# ── Post to Neuron soul ─────────────────────────────────────────────────────────
|
||||||
|
if [[ "$DRY_RUN" -eq 1 ]]; then
|
||||||
|
ok "Dry-run complete — would POST ${CHAR_COUNT} chars to ${SOUL_URL}/api/neuron/memory"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Posting to Neuron soul at ${SOUL_URL} ..."
|
||||||
|
|
||||||
|
PAYLOAD=$(python3 -c "
|
||||||
|
import json, sys
|
||||||
|
content = sys.argv[1]
|
||||||
|
label = sys.argv[2]
|
||||||
|
tags = ['photo-import', 'ocr', 'glm-ocr']
|
||||||
|
print(json.dumps({'content': content, 'label': label, 'tags': tags}))
|
||||||
|
" "$OCR_TEXT" "Photo: ${FILENAME}")
|
||||||
|
|
||||||
|
HTTP_STATUS=$(curl -s -o /tmp/photo-to-memory-response.json -w "%{http_code}" \
|
||||||
|
-X POST "${SOUL_URL}/api/neuron/memory" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "$PAYLOAD")
|
||||||
|
|
||||||
|
if [[ "$HTTP_STATUS" =~ ^2 ]]; then
|
||||||
|
NODE_ID=$(python3 -c "
|
||||||
|
import json, sys
|
||||||
|
try:
|
||||||
|
d = json.load(open('/tmp/photo-to-memory-response.json'))
|
||||||
|
print(d.get('id', d.get('node_id', 'unknown')))
|
||||||
|
except Exception:
|
||||||
|
print('unknown')
|
||||||
|
")
|
||||||
|
ok "Memory node created: ${NODE_ID}"
|
||||||
|
ok "Label: Photo: ${FILENAME}"
|
||||||
|
ok "Tags: photo-import, ocr, glm-ocr"
|
||||||
|
else
|
||||||
|
BODY=$(cat /tmp/photo-to-memory-response.json 2>/dev/null || echo "(no body)")
|
||||||
|
die "Soul returned HTTP ${HTTP_STATUS}: ${BODY}"
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user