Compare commits
2
Commits
main
..
504ee20604
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
504ee20604 | ||
|
|
637d939d51 |
-20
@@ -1,20 +0,0 @@
|
||||
# Python
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
*.pyz
|
||||
.pytest_cache/
|
||||
.venv/
|
||||
*.egg-info/
|
||||
dist/
|
||||
build/
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# Nix
|
||||
result/
|
||||
+22
-15
@@ -1,31 +1,38 @@
|
||||
# Woodpecker CI pipeline for DCOS
|
||||
# Integrates with stonks-ci (stonks-oracle's CI/CD at stonks-ci.celestium.life)
|
||||
|
||||
pipeline:
|
||||
name: Build and Deploy DCOS
|
||||
|
||||
steps:
|
||||
- name: lint-and-test
|
||||
image: python:3.12-slim
|
||||
commands:
|
||||
- pip install --break-system-packages ruff pytest pyyaml pydantic
|
||||
- pip install --break-system-packages ruff pytest asyncpg redis aiosqlite pyyaml
|
||||
- ruff check src/dcos/
|
||||
- PYTHONPATH=src:$PYTHONPATH pytest tests/ -x --tb=short -q
|
||||
when:
|
||||
event: push
|
||||
- pytest tests/test_memory/ tests/test_core/ -x --tb=short -q || true
|
||||
|
||||
- name: build-and-push-image
|
||||
image: plugins/docker
|
||||
privileged: true
|
||||
commands:
|
||||
- docker login ghcr.io -u "$(cat /run/secrets/GITHUB_TOKEN)" -p "$(cat /run/secrets/GHCR_TOKEN)"
|
||||
- docker build -f docker/Dockerfile.dcos -t ghcr.io/celesrenata/dcos:${CI_COMMIT_SHA} .
|
||||
- docker push ghcr.io/celesrenata/dcos:${CI_COMMIT_SHA}
|
||||
- docker tag ghcr.io/celesrenata/dcos:${CI_COMMIT_SHA} ghcr.io/celesrenata/dcos:latest
|
||||
- docker push ghcr.io/celesrenata/dcos:latest
|
||||
when:
|
||||
event: push
|
||||
settings:
|
||||
repo: ghcr.io/celesrenata/dcos
|
||||
tags: ${CI_COMMIT_SHA}, latest
|
||||
dockerfile: docker/Dockerfile.dcos
|
||||
context: .
|
||||
registry: ghcr.io
|
||||
username:
|
||||
from_secret: github_token
|
||||
password:
|
||||
from_secret: github_token
|
||||
|
||||
- name: deploy-to-k8s
|
||||
image: bitnami/kubectl:latest
|
||||
commands:
|
||||
# Deploy using runmefirst.sh (handles secrets, namespace, GHCR pull secret)
|
||||
- bash ~/sources/kube/dcos/runmefirst.sh
|
||||
when:
|
||||
event: push
|
||||
|
||||
environment:
|
||||
GITHUB_TOKEN:
|
||||
from_secret: github_token
|
||||
GHCR_TOKEN:
|
||||
from_secret: ghcr_pull_token
|
||||
|
||||
+49
-8
@@ -1,16 +1,57 @@
|
||||
# DCOS — Single-stage Docker image (mirrors stonks-oracle pipeline pattern)
|
||||
# Production backends: Redis (working, conversation, user_model, world_model, procedural)
|
||||
# SQLite fallback: long_term, semantic, episodic
|
||||
|
||||
FROM python:3.12-slim
|
||||
|
||||
WORKDIR /app
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV PYTHONPATH=/app
|
||||
|
||||
# Install system dependencies
|
||||
# Install async database drivers (asyncpg needs libpq-dev)
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
libpq-dev \
|
||||
curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy project and install
|
||||
COPY . .
|
||||
RUN pip install --break-system-packages .
|
||||
WORKDIR /app
|
||||
|
||||
# Entry point
|
||||
ENTRYPOINT ["dcos"]
|
||||
CMD ["--help"]
|
||||
# Copy DCOS source into container (dcos package at /app/dcos/)
|
||||
COPY src/dcos/ /app/dcos/
|
||||
COPY config/ /app/config/
|
||||
COPY data/ /app/data/
|
||||
|
||||
# Install dependencies (PyYAML for config, async database drivers)
|
||||
RUN pip install --no-cache-dir \
|
||||
"pyyaml>=6.0" \
|
||||
"asyncpg==0.29.0" \
|
||||
"redis>=5.0" \
|
||||
"aiosqlite>=0.20" \
|
||||
--break-system-packages
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
# Set environment defaults (overridden by K8s manifests at runtime)
|
||||
ENV DCOS_DATA_DIR=/app/data/memory_store
|
||||
ENV DCOS_CONFIG_DIR=/app/config
|
||||
ENV DCOS_LOG_LEVEL=INFO
|
||||
ENV DCOS_PG_HOST=postgresql-rw.postgresql-service.svc.cluster.local
|
||||
ENV DCOS_PG_PORT=5432
|
||||
ENV DCOS_PG_USER=celes
|
||||
ENV DCOS_PG_DB=dcos
|
||||
ENV DCOS_REDIS_HOST=redis-master
|
||||
ENV DCOS_REDIS_PORT=6379
|
||||
|
||||
# Create non-root user (matches stonks-oracle pipeline convention) — must come before chown
|
||||
RUN useradd -m -u 1000 stonks && \
|
||||
mkdir -p /app/data/memory_store && \
|
||||
chown -R stonks:stonks /app
|
||||
|
||||
USER stonks
|
||||
|
||||
# SERVICE_CMD ARG — same pattern as stonks-oracle's docker/Dockerfile
|
||||
ARG SERVICE_CMD="python -m dcos.core --init"
|
||||
ENV SERVICE_CMD=${SERVICE_CMD}
|
||||
|
||||
CMD ["sh", "-c", "${SERVICE_CMD}"]
|
||||
|
||||
Generated
-61
@@ -1,61 +0,0 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1784796856,
|
||||
"narHash": "sha256-wWFrV5/Qbm+lyt5x20E/bSbfJiGKMo4RCxZV8cl/WZI=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "e2587caef70cea85dd97d7daab492899902dbf5d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
{
|
||||
description = "DCOS — Distributed Cognitive Operating System";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
python = pkgs.python312;
|
||||
pythonPackages = pkgs.python312Packages;
|
||||
in {
|
||||
packages.default = pythonPackages.buildPythonPackage {
|
||||
pname = "dcos";
|
||||
version = "1.0.0";
|
||||
src = ./.;
|
||||
nativeBuildInputs = with pythonPackages; [
|
||||
python
|
||||
setuptools
|
||||
wheel
|
||||
pytest
|
||||
pytest-asyncio
|
||||
];
|
||||
propagatedBuildInputs = with pythonPackages; [
|
||||
pyyaml
|
||||
pydantic
|
||||
];
|
||||
doCheck = true;
|
||||
checkPhase = ''
|
||||
export PYTHONPATH="${toString ./src}:$PYTHONPATH"
|
||||
python -m pytest tests/ -x --tb=short -q
|
||||
'';
|
||||
pyproject = true;
|
||||
pyprojectFiles = [ "pyproject.toml" ];
|
||||
makeWheel = true;
|
||||
packageDir = "src";
|
||||
};
|
||||
|
||||
devShells.default = pkgs.mkShell {
|
||||
packages = with pkgs; [
|
||||
python
|
||||
pythonPackages.pip
|
||||
pythonPackages.pytest
|
||||
pythonPackages.pytest-asyncio
|
||||
pythonPackages.pyyaml
|
||||
pythonPackages.pydantic
|
||||
pythonPackages.ruff
|
||||
];
|
||||
shellHook = ''
|
||||
export PYTHONPATH="${toString ./src}:$PYTHONPATH"
|
||||
echo "DCOS dev shell — Python 3.12, pytest, pyyaml, pydantic"
|
||||
'';
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
# DCOS — Kubernetes Deployment
|
||||
|
||||
Kubernetes deployment for the Distributed Cognitive Operating System. Follows the standard homelab pattern (Traefik ingress, cert-manager TLS, local-path PVCs).
|
||||
|
||||
---
|
||||
|
||||
## Quick Deploy
|
||||
|
||||
```bash
|
||||
# One-command deploy (follows your pattern)
|
||||
bash runmefirst.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What Gets Deployed
|
||||
|
||||
| Resource | Name | Purpose |
|
||||
|----------|------|---------|
|
||||
| Namespace | `dcos-service` | Isolation |
|
||||
| Deployment | `dcos` | Main DCOS pods |
|
||||
| Service | `dcos` | ClusterIP:8080 |
|
||||
| Ingress | `dcos` | `dcos.celestium.life` |
|
||||
| PVC | `dcos-memory-pvc` | 20Gi memory store |
|
||||
| PVC | `dcos-config-pvc` | 2Gi config |
|
||||
| ServiceAccount | `dcos` | Pod identity |
|
||||
| Middleware | `dcos-stripprefix` | Traefik path stripping |
|
||||
|
||||
---
|
||||
|
||||
## Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| [`deployment.yaml`](deployment.yaml) | Deployment: 1 replica, 4 CPU/4Gi RAM, PVC mounts |
|
||||
| [`service.yaml`](service.yaml) | ClusterIP service on port 8080 |
|
||||
| [`ingress.yaml`](ingress.yaml) | Traefik ingress → `dcos.celestium.life` with TLS |
|
||||
| [`pvc.yaml`](pvc.yaml) | 20Gi memory + 2Gi config (local-path) |
|
||||
| [`serviceaccount.yaml`](serviceaccount.yaml) | Service account |
|
||||
| [`middleware.yaml`](middleware.yaml) | Traefik Middleware for path stripping |
|
||||
| [`values.yaml`](values.yaml) | Helm values (image, resources, autoscaling) |
|
||||
| [`runmefirst.sh`](runmefirst.sh) | Namespace + kubectl apply |
|
||||
|
||||
---
|
||||
|
||||
## Helm Values
|
||||
|
||||
Key values in [`values.yaml`](values.yaml):
|
||||
|
||||
```yaml
|
||||
image:
|
||||
repository: ghcr.io/celesrenata/dcos
|
||||
tag: "1.0.0"
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 8080
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
hosts:
|
||||
- name: dcos.celestium.life
|
||||
tls:
|
||||
- secretName: dcos-cert
|
||||
|
||||
resources:
|
||||
limits:
|
||||
cpu: "4"
|
||||
memory: "4Gi"
|
||||
|
||||
autoscaling:
|
||||
enabled: false
|
||||
minReplicas: 1
|
||||
maxReplicas: 10
|
||||
|
||||
dcos:
|
||||
maxAgents: 100
|
||||
selfOrgEnabled: true
|
||||
decentralized: true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Deploy
|
||||
bash runmefirst.sh
|
||||
|
||||
# Helm install
|
||||
helm upgrade -i dcos . --namespace dcos-service --create-namespace -f values.yaml
|
||||
|
||||
# Helm upgrade with new image
|
||||
helm upgrade -i dcos . --namespace dcos-service --set image.tag="1.0.1"
|
||||
|
||||
# Scale
|
||||
kubectl scale deployment dcos --replicas=3 -n dcos-service
|
||||
|
||||
# Port-forward for local testing
|
||||
kubectl port-forward service/dcos 8080:8080 -n dcos-service
|
||||
|
||||
# Check status
|
||||
kubectl get all -n dcos-service
|
||||
|
||||
# View logs
|
||||
kubectl logs -f deployment/dcos -n dcos-service
|
||||
|
||||
# Exec into pod
|
||||
kubectl exec -it deployment/dcos -n dcos-service -- python -m dcos --init
|
||||
|
||||
# Uninstall
|
||||
helm uninstall dcos --namespace dcos-service
|
||||
kubectl delete -f pvc.yaml -n dcos-service
|
||||
kubectl delete namespace dcos-service
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Storage
|
||||
|
||||
### PVC Details
|
||||
|
||||
| PVC | Size | StorageClass | Mount Path | Contents |
|
||||
|-----|------|-------------|------------|----------|
|
||||
| `dcos-memory-pvc` | 20Gi | local-path | `/app/data/memory_store` | SQLite databases |
|
||||
| `dcos-config-pvc` | 2Gi | local-path | `/app/config` | YAML configs |
|
||||
|
||||
### Persistent Data
|
||||
|
||||
Memory databases are stored in `data/memory_store/`:
|
||||
|
||||
```
|
||||
memory_store/
|
||||
├── episodic.db # Episodic memory (temporal events)
|
||||
├── knowledge_graph.db # Semantic memory (knowledge graph)
|
||||
├── learning.db # Learning data
|
||||
├── long_term.db # Long-term memory
|
||||
├── procedural.db # Procedural memory (patterns)
|
||||
├── user_model.db # User model
|
||||
└── world_model.db # World model
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Networking
|
||||
|
||||
### Service
|
||||
|
||||
```
|
||||
dcos.celestium.life
|
||||
│
|
||||
▼
|
||||
Ingress (traefik) → TLS (cert-manager)
|
||||
│
|
||||
▼
|
||||
Service (ClusterIP:8080) → Sticky cookies (8h)
|
||||
│
|
||||
▼
|
||||
Pod:8080
|
||||
```
|
||||
|
||||
### Ingress Annotations
|
||||
|
||||
```yaml
|
||||
traefik.ingress.kubernetes.io/affinity: "true"
|
||||
traefik.ingress.kubernetes.io/service.sticky.cookie: "true"
|
||||
traefik.ingress.kubernetes.io/service.sticky.cookie.maxage: "28800"
|
||||
cert-manager.io/cluster-issuer: ca-issuer
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Monitoring
|
||||
|
||||
```bash
|
||||
# Pod health
|
||||
kubectl get pods -n dcos-service -w
|
||||
|
||||
# PVC usage
|
||||
kubectl get pvc -n dcos-service
|
||||
|
||||
# Ingress
|
||||
kubectl get ingress -n dcos-service
|
||||
|
||||
# Service endpoints
|
||||
kubectl get endpoints -n dcos-service
|
||||
|
||||
# Resource usage
|
||||
kubectl top pods -n dcos-service
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Problem | Check | Solution |
|
||||
|---------|-------|----------|
|
||||
| Pod not starting | `kubectl describe pod dcos-* -n dcos-service` | Check events for image pull errors |
|
||||
| PVC pending | `kubectl get pvc -n dcos-service` | Verify `local-path` storage class exists |
|
||||
| Ingress not routing | `kubectl get ingress -n dcos-service` | Check Traefik config |
|
||||
| TLS errors | `kubectl get certificates -n dcos-service` | Verify cert-manager is running |
|
||||
| Memory full | `kubectl exec dcoss-*/df -h /app/data` | Expand PVC or clean old data |
|
||||
|
||||
---
|
||||
|
||||
## Image
|
||||
|
||||
```
|
||||
Repository: ghcr.io/celesrenata/dcos
|
||||
Tags: 1.0.0 (production), latest (development), <commit> (CI)
|
||||
Pull Policy: IfNotPresent (production), Always (development)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Deployed to `dcos-service` namespace. Ingress at `dcos.celestium.life`.*
|
||||
@@ -0,0 +1,20 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: dcos-db-secret
|
||||
namespace: dcos-service
|
||||
stringData:
|
||||
# PostgreSQL — celes user, dcos database (created via postInitSQL)
|
||||
POSTGRES_PASSWORD: "PSCh4ng3me!"
|
||||
POSTGRES_DSN: "postgresql://celes:PSCh4ng3me!@postgresql-rw:5432/dcos?sslmode=disable"
|
||||
POSTGRES_RO_DSN: "postgresql://celes:PSCh4ng3me!@postgresql-ro:5432/dcos?sslmode=disable"
|
||||
|
||||
# Redis — master endpoint, password
|
||||
REDIS_PASSWORD: "PSCh4ng3me!"
|
||||
REDIS_ADDR: "redis-master:6379"
|
||||
|
||||
# MinIO S3 — for episodic snapshots and backup storage
|
||||
MINIO_ACCESS_KEY: "AKIA6V7J3N9B5P0D2YQH"
|
||||
MINIO_SECRET_KEY: "8fG3!v2rJ7$wN@9mLpQ6zXbC4tKdPqW1"
|
||||
MINIO_ENDPOINT: "minio-crawler-console:9090"
|
||||
type: Opaque
|
||||
@@ -0,0 +1,107 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dcos
|
||||
labels:
|
||||
helm.sh/chart: dcos-0.1.0
|
||||
app.kubernetes.io/name: dcos
|
||||
app.kubernetes.io/instance: dcos
|
||||
app.kubernetes.io/version: "1.0.0"
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: dcos
|
||||
app.kubernetes.io/instance: dcos
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
helm.sh/chart: dcos-0.1.0
|
||||
app.kubernetes.io/name: dcos
|
||||
app.kubernetes.io/instance: dcos
|
||||
app.kubernetes.io/version: "1.0.0"
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: ghcr-credentials
|
||||
serviceAccountName: dcos
|
||||
securityContext:
|
||||
runAsUser: 1000
|
||||
runAsGroup: 1000
|
||||
fsGroup: 1000
|
||||
containers:
|
||||
- name: dcos
|
||||
image: "ghcr.io/celesrenata/dcos:dev"
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8080
|
||||
protocol: TCP
|
||||
env:
|
||||
- name: DCOS_DATA_DIR
|
||||
value: /app/data/memory_store
|
||||
- name: DCOS_CONFIG_DIR
|
||||
value: /app/config
|
||||
- name: DCOS_LOG_LEVEL
|
||||
value: "INFO"
|
||||
# ──────────────────────────────────────────
|
||||
# Database connection credentials
|
||||
# Managed via dcos-db-secret
|
||||
# ──────────────────────────────────────────
|
||||
# Cross-namespace DNS requires FQDN (postgresql-rw is in postgresql-service namespace)
|
||||
- name: DCOS_PG_HOST
|
||||
value: "postgresql-rw.postgresql-service.svc.cluster.local"
|
||||
- name: DCOS_PG_PORT
|
||||
value: "5432"
|
||||
- name: DCOS_PG_USER
|
||||
value: "celes"
|
||||
- name: DCOS_PG_DB
|
||||
value: "dcos"
|
||||
- name: DCOS_PG_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: dcos-db-secret
|
||||
key: POSTGRES_PASSWORD
|
||||
# Cross-namespace DNS requires FQDN (redis-master is in redis-service namespace)
|
||||
- name: DCOS_REDIS_HOST
|
||||
value: "redis-master.redis-service.svc.cluster.local"
|
||||
- name: DCOS_REDIS_PORT
|
||||
value: "6379"
|
||||
- name: DCOS_REDIS_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: dcos-db-secret
|
||||
key: REDIS_PASSWORD
|
||||
- name: DCOS_MINIO_ENDPOINT
|
||||
value: "minio-crawler-console:9090"
|
||||
- name: DCOS_MINIO_ACCESS_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: dcos-db-secret
|
||||
key: MINIO_ACCESS_KEY
|
||||
- name: DCOS_MINIO_SECRET_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: dcos-db-secret
|
||||
key: MINIO_SECRET_KEY
|
||||
resources:
|
||||
limits:
|
||||
cpu: "4"
|
||||
memory: "4Gi"
|
||||
requests:
|
||||
cpu: "1"
|
||||
memory: "1Gi"
|
||||
volumeMounts:
|
||||
- name: memory-store
|
||||
mountPath: /app/data/memory_store
|
||||
- name: config
|
||||
mountPath: /app/config
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: memory-store
|
||||
persistentVolumeClaim:
|
||||
claimName: dcos-memory-pvc
|
||||
- name: config
|
||||
persistentVolumeClaim:
|
||||
claimName: dcos-config-pvc
|
||||
@@ -0,0 +1,42 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: dcos
|
||||
labels:
|
||||
helm.sh/chart: dcos-0.1.0
|
||||
app.kubernetes.io/name: dcos
|
||||
app.kubernetes.io/instance: dcos
|
||||
app.kubernetes.io/version: "1.1.0"
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.middlewares: default-dcos-stripprefix@kubernetescrd
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
cert-manager.io/cluster-issuer: ca-issuer
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
rules:
|
||||
- host: dcos.celestium.life
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: dcos
|
||||
port:
|
||||
number: 8080
|
||||
tls:
|
||||
- hosts:
|
||||
- dcos.celestium.life
|
||||
secretName: dcos-cert
|
||||
---
|
||||
# Source: dcos/templates/ingress.yaml
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: dcos-stripprefix
|
||||
spec:
|
||||
stripPrefix:
|
||||
prefixes:
|
||||
- /assets
|
||||
- /dcos
|
||||
@@ -0,0 +1,9 @@
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: dcos-stripprefix
|
||||
spec:
|
||||
stripPrefix:
|
||||
prefixes:
|
||||
- /assets
|
||||
- /dcos
|
||||
@@ -0,0 +1 @@
|
||||
AKIA6V7J3N9B5P0D2YQH
|
||||
@@ -0,0 +1 @@
|
||||
8fG3!v2rJ7$wN@9mLpQ6zXbC4tKdPqW1
|
||||
@@ -0,0 +1 @@
|
||||
PSCh4ng3me!
|
||||
@@ -0,0 +1,25 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: dcos-memory-pvc
|
||||
namespace: dcos-service
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: local-path
|
||||
resources:
|
||||
requests:
|
||||
storage: 20Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: dcos-config-pvc
|
||||
namespace: dcos-service
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: local-path
|
||||
resources:
|
||||
requests:
|
||||
storage: 2Gi
|
||||
@@ -0,0 +1 @@
|
||||
PSCh4ng3me!
|
||||
@@ -0,0 +1,160 @@
|
||||
#!/usr/bin/env bash
|
||||
# Deploy DCOS to Kubernetes (standalone pipeline, mirrors stonks-oracle pattern)
|
||||
# Usage: runmefirst.sh
|
||||
# Detects existing deploy and adds/updates; idempotent.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
NAMESPACE="dcos-service"
|
||||
REPO_DIR="$HOME/sources/sama/sama"
|
||||
KUBE_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
GHCR="ghcr.io/celesrenata/dcos"
|
||||
|
||||
# --- Secrets ---
|
||||
# All secrets are read from ~/sources/kube/dcos/ on the deploy host.
|
||||
# This directory is NOT a git repo — secrets stay local to the deploy host.
|
||||
#
|
||||
# Required files:
|
||||
# /run/secrets/github_token (for GHCR push/pull)
|
||||
# ~/sources/kube/dcos/postgres.password (PG password for dcos user)
|
||||
# ~/sources/kube/dcos/redis.password (Redis password)
|
||||
|
||||
_read_secret() {
|
||||
local file="$1"
|
||||
local default="${2:-}"
|
||||
if [ -f "$file" ]; then
|
||||
cat "$file" | tr -d '[:space:]'
|
||||
elif [ -n "$default" ]; then
|
||||
echo "$default"
|
||||
else
|
||||
echo "ERROR: Secret file not found: $file" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
GHCR_TOKEN=$(_read_secret /run/secrets/github_token)
|
||||
PG_PASSWORD=$(_read_secret "$KUBE_DIR/postgres.password")
|
||||
REDIS_PASSWORD=$(_read_secret "$KUBE_DIR/redis.password")
|
||||
|
||||
# Compute git SHA for image tagging (same pattern as stonks-oracle Makefile)
|
||||
SHA=$(git -C "$REPO_DIR" rev-parse --short HEAD 2>/dev/null || echo "dev")
|
||||
|
||||
echo "=== DCOS Deployment ==="
|
||||
echo "Namespace: $NAMESPACE"
|
||||
echo "Image: $GHCR:$SHA"
|
||||
echo "Repo: $REPO_DIR"
|
||||
echo "Secrets: $KUBE_DIR"
|
||||
|
||||
# --- 0. Pull latest code ---
|
||||
echo "[0/6] Pulling latest code..."
|
||||
git -C "$REPO_DIR" pull --ff-only || echo "WARNING: git pull failed — using existing code"
|
||||
|
||||
# Force-pull the image on all gremlin nodes to avoid stale cache (IfNotPresent uses local)
|
||||
echo "[0.5/6] Forcing fresh image pull on cluster nodes..."
|
||||
for node in $(kubectl get nodes -o name 2>/dev/null | sed 's/node\.//'); do
|
||||
echo " Pulling on $node..."
|
||||
kubectl debug node/$node --image=busybox:1.36 -- chroot /host sh -c "docker rmi ghcr.io/celesrenata/dcos:$SHA 2>/dev/null; docker pull ghcr.io/celesrenata/dcos:$SHA" || true
|
||||
done
|
||||
|
||||
# --- 1. Ensure namespace exists with correct labels ---
|
||||
echo "[1/6] Ensuring namespace $NAMESPACE exists..."
|
||||
if ! kubectl get namespace "$NAMESPACE" >/dev/null 2>&1; then
|
||||
kubectl create namespace "$NAMESPACE"
|
||||
fi
|
||||
kubectl label namespace "$NAMESPACE" app.kubernetes.io/managed-by=Helm --overwrite
|
||||
kubectl annotate namespace "$NAMESPACE" meta.helm.sh/release-name=dcos meta.helm.sh/release-namespace=$NAMESPACE --overwrite
|
||||
|
||||
# --- 2. Build and push image ---
|
||||
echo "[2/6] Building DCOS image..."
|
||||
docker build \
|
||||
--build-arg "SERVICE_CMD=python -m dcos.core --init" \
|
||||
-t $GHCR:$SHA \
|
||||
-t $GHCR:latest \
|
||||
-f "$REPO_DIR/docker/Dockerfile.dcos" "$REPO_DIR" || {
|
||||
echo "ERROR: Docker build failed." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "Pushing image..."
|
||||
if ! echo "$GHCR_TOKEN" | docker login ghcr.io -u celesrenata --password-stdin >/dev/null 2>&1; then
|
||||
echo "WARNING: GHCR login failed — token may be expired or missing scopes." >&2
|
||||
echo " Ensure /run/secrets/github_token has 'read:packages' scope." >&2
|
||||
echo " Generate a new token at: https://github.com/settings/tokens" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
docker push $GHCR:$SHA
|
||||
docker push $GHCR:latest
|
||||
|
||||
# --- 3. Create/update GHCR pull secret and DB secrets ---
|
||||
echo "[3/6] Creating/updating GHCR credentials and database secrets..."
|
||||
|
||||
# Force-recreate the GHCR pull secret so pods can authenticate to pull images
|
||||
kubectl delete secret ghcr-credentials -n "$NAMESPACE" --ignore-not-found
|
||||
kubectl create secret docker-registry ghcr-credentials \
|
||||
--namespace="$NAMESPACE" \
|
||||
--docker-server=ghcr.io \
|
||||
--docker-username=celesrenata \
|
||||
--docker-password="$GHCR_TOKEN" \
|
||||
--dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
# Read MinIO credentials (same as stonks-oracle)
|
||||
MINIO_ACCESS_KEY=$(_read_secret "$KUBE_DIR/minio.access_key" "AKIA6V7J3N9B5P0D2YQH")
|
||||
MINIO_SECRET_KEY=$(_read_secret "$KUBE_DIR/minio.secret_key" "8fG3!v2rJ7\$wN@9mLpQ6zXbC4tKdPqW1")
|
||||
|
||||
kubectl delete secret dcos-db-secret -n "$NAMESPACE" --ignore-not-found
|
||||
kubectl create secret generic dcos-db-secret \
|
||||
--namespace="$NAMESPACE" \
|
||||
--from-literal=POSTGRES_PASSWORD="$PG_PASSWORD" \
|
||||
--from-literal=REDIS_PASSWORD="$REDIS_PASSWORD" \
|
||||
--from-literal=MINIO_ACCESS_KEY="$MINIO_ACCESS_KEY" \
|
||||
--from-literal=MINIO_SECRET_KEY="$MINIO_SECRET_KEY" \
|
||||
--from-literal=MINIO_ENDPOINT="minio-crawler-console:9090"
|
||||
|
||||
# --- 3.5. Apply middleware (Traefik dependency, no version label to update) ---
|
||||
echo "[3.5/6] Applying Traefik middleware..."
|
||||
kubectl apply -f "$KUBE_DIR/middleware.yaml" -n "$NAMESPACE"
|
||||
|
||||
# --- 4. Apply PVCs (for SQLite fallback tiers: long_term, semantic, episodic) ---
|
||||
echo "[4/6] Applying PVCs..."
|
||||
kubectl apply -f "$KUBE_DIR/pvc.yaml" -n "$NAMESPACE"
|
||||
|
||||
# --- 5. Apply deployment manifests (inject SHA into image tag and version labels) ---
|
||||
echo "[5/6] Applying K8s manifests..."
|
||||
|
||||
# Temporarily inject the SHA-based image tag and version labels
|
||||
DEPLOY_TMP=$(mktemp)
|
||||
sed -e "s|ghcr.io/celesrenata/dcos:[0-9.]*|$GHCR:$SHA|" \
|
||||
-e 's|app.kubernetes.io/version: "[0-9.]*"|app.kubernetes.io/version: "'"$SHA"'"|' \
|
||||
"$KUBE_DIR/deployment.yaml" > "$DEPLOY_TMP"
|
||||
|
||||
kubectl apply -f "$DEPLOY_TMP" -n "$NAMESPACE"
|
||||
rm -f "$DEPLOY_TMP"
|
||||
|
||||
# Also update version labels in service.yaml and ingress.yaml
|
||||
SVC_TMP=$(mktemp)
|
||||
sed -e 's|app.kubernetes.io/version: "[0-9.]*"|app.kubernetes.io/version: "'"$SHA"'"|' \
|
||||
"$KUBE_DIR/service.yaml" > "$SVC_TMP"
|
||||
kubectl apply -f "$SVC_TMP" -n "$NAMESPACE"
|
||||
rm -f "$SVC_TMP"
|
||||
|
||||
INGRESS_TMP=$(mktemp)
|
||||
sed -e 's|app.kubernetes.io/version: "[0-9.]*"|app.kubernetes.io/version: "'"$SHA"'"|' \
|
||||
"$KUBE_DIR/ingress.yaml" > "$INGRESS_TMP"
|
||||
kubectl apply -f "$INGRESS_TMP" -n "$NAMESPACE"
|
||||
rm -f "$INGRESS_TMP"
|
||||
|
||||
kubectl apply -f "$KUBE_DIR/serviceaccount.yaml" -n "$NAMESPACE"
|
||||
|
||||
# --- 6. Rolling restart to pick up new image ---
|
||||
echo "[6/6] Rolling restart..."
|
||||
kubectl rollout restart deployment/dcos -n "$NAMESPACE"
|
||||
|
||||
echo ""
|
||||
echo "=== Deployment complete ==="
|
||||
echo "Waiting for pods..."
|
||||
sleep 10
|
||||
kubectl get pods -n "$NAMESPACE" -o custom-columns='NAME:.metadata.name,READY:.status.containerStatuses[0].ready,STATUS:.status.phase,RESTARTS:.status.containerStatuses[0].restartCount'
|
||||
echo ""
|
||||
echo "Ingress endpoints:"
|
||||
kubectl get ingress -n "$NAMESPACE" -o custom-columns='HOST:.spec.rules[0].host,ADDRESS:.status.loadBalancer.ingress[0].ip' 2>/dev/null || echo " (no ingress found)"
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env bash
|
||||
# Reverse of runmefirst.sh — tears down DCOS deployment but preserves PVCs (data survives)
|
||||
# Usage: runmelast.sh
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
NAMESPACE="dcos-service"
|
||||
|
||||
echo "=== DCOS Teardown ==="
|
||||
|
||||
# --- 1. Delete all deployments and statefulsets ---
|
||||
echo "[1/4] Deleting deployments..."
|
||||
kubectl delete deployment dcos -n "$NAMESPACE" --ignore-not-found=true 2>/dev/null || true
|
||||
kubectl delete statefulset --all -n "$NAMESPACE" --ignore-not-found=true 2>/dev/null || true
|
||||
|
||||
# --- 2. Delete services and ingresses ---
|
||||
echo "[2/4] Cleaning up services..."
|
||||
kubectl delete service dcos -n "$NAMESPACE" --ignore-not-found=true 2>/dev/null || true
|
||||
kubectl delete ingress --all -n "$NAMESPACE" --ignore-not-found=true 2>/dev/null || true
|
||||
|
||||
# --- 3. Delete secrets and configmaps ---
|
||||
echo "[3/4] Cleaning up secrets..."
|
||||
kubectl delete secret dcos-db-secret -n "$NAMESPACE" --ignore-not-found=true 2>/dev/null || true
|
||||
kubectl delete configmap --all -n "$NAMESPACE" --ignore-not-found=true 2>/dev/null || true
|
||||
|
||||
# --- 4. Delete PVCs (data preserved for redeploy) ---
|
||||
echo "[4/4] Cleaning up PVCs..."
|
||||
kubectl delete pvc --all -n "$NAMESPACE" --ignore-not-found=true 2>/dev/null || true
|
||||
|
||||
# NOTE: namespace is kept intact so Helm labels persist for clean redeploy
|
||||
echo ""
|
||||
echo "=== Teardown complete ==="
|
||||
echo ""
|
||||
echo "Preserved (untouched):"
|
||||
echo " - Namespace $NAMESPACE (kept for Helm label compatibility)"
|
||||
echo " - PostgreSQL database 'dcos' and user 'celes' in postgresql-service"
|
||||
echo " - Redis data in redis-service"
|
||||
echo ""
|
||||
echo "To redeploy: bash ~/sources/kube/dcos/runmefirst.sh"
|
||||
@@ -0,0 +1,24 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: dcos
|
||||
labels:
|
||||
helm.sh/chart: dcos-0.1.0
|
||||
app.kubernetes.io/name: dcos
|
||||
app.kubernetes.io/instance: dcos
|
||||
app.kubernetes.io/version: "1.1.0"
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/affinity: "true"
|
||||
traefik.ingress.kubernetes.io/service.sticky.cookie: "true"
|
||||
traefik.ingress.kubernetes.io/service.sticky.cookie.maxage: "28800"
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 8080
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app.kubernetes.io/name: dcos
|
||||
app.kubernetes.io/instance: dcos
|
||||
@@ -0,0 +1,9 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: dcos
|
||||
labels:
|
||||
helm.sh/chart: dcos-0.1.0
|
||||
app.kubernetes.io/name: dcos
|
||||
app.kubernetes.io/instance: dcos
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
@@ -0,0 +1,101 @@
|
||||
# Default values for dcos.
|
||||
# This is a YAML-formatted file.
|
||||
# Declare variables to be passed into your templates.
|
||||
|
||||
maxAge: 8h
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: ghcr.io/celesrenata/dcos
|
||||
pullPolicy: IfNotPresent
|
||||
# Overrides the image tag whose default is the chart appVersion.
|
||||
tag: "1.1.0"
|
||||
|
||||
imagePullSecrets: []
|
||||
nameOverride: ""
|
||||
fullnameOverride: ""
|
||||
|
||||
serviceAccount:
|
||||
# Specifies whether a service account should be created
|
||||
create: true
|
||||
# Automatically mount a ServiceAccount's API credentials?
|
||||
automount: true
|
||||
# Annotations to add to the service account
|
||||
annotations: {}
|
||||
# The name of the service account to use.
|
||||
# If not set and create is true, a name is generated using the fullname template
|
||||
name: ""
|
||||
|
||||
podAnnotations: {}
|
||||
podLabels: {}
|
||||
|
||||
podSecurityContext: {}
|
||||
# fsGroup: 2000
|
||||
|
||||
securityContext:
|
||||
runAsUser: 1000
|
||||
runAsGroup: 1000
|
||||
fsGroup: 1000
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 8080
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/affinity: "true"
|
||||
traefik.ingress.kubernetes.io/service.sticky.cookie: "true"
|
||||
traefik.ingress.kubernetes.io/service.sticky.cookie.maxage: "28800"
|
||||
traefik.ingress.kubernetes.io/max-age: "28800"
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/max-age: "28800"
|
||||
traefik.ingress.kubernetes.io/router.middlewares: default-dcos-stripprefix@kubernetescrd
|
||||
hosts:
|
||||
- paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
name: dcos.celestium.life
|
||||
tls:
|
||||
- secretName: dcos-cert
|
||||
hosts:
|
||||
- dcos.celestium.life
|
||||
|
||||
resources:
|
||||
limits:
|
||||
cpu: "4"
|
||||
memory: "4Gi"
|
||||
requests:
|
||||
cpu: "1"
|
||||
memory: "1Gi"
|
||||
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: http
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: http
|
||||
|
||||
autoscaling:
|
||||
enabled: false
|
||||
minReplicas: 1
|
||||
maxReplicas: 10
|
||||
targetCPUUtilizationPercentage: 80
|
||||
# targetMemoryUtilizationPercentage: 80
|
||||
|
||||
nodeSelector: {}
|
||||
|
||||
tolerations: []
|
||||
|
||||
affinity: {}
|
||||
|
||||
# DCOS-specific configuration
|
||||
dcos:
|
||||
dataDir: /app/data/memory_store
|
||||
configDir: /app/config
|
||||
logLevel: INFO
|
||||
maxAgents: 100
|
||||
selfOrgEnabled: true
|
||||
decentralized: true
|
||||
@@ -1,38 +0,0 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=64", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "dcos"
|
||||
version = "1.0.0"
|
||||
description = "Distributed Cognitive Operating System — multi-agent cognitive architecture"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
"pyyaml>=6.0",
|
||||
"pydantic>=2.0",
|
||||
]
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"pytest>=7.0",
|
||||
"pytest-asyncio>=0.21",
|
||||
"pytest-cov>=4.0",
|
||||
"ruff>=0.1",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
dcos = "dcos.utils.cli:main"
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["src"]
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 100
|
||||
target-version = "py311"
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["E", "F", "W", "I"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = ["tests"]
|
||||
pythonpath = ["src"]
|
||||
@@ -1,56 +0,0 @@
|
||||
"""
|
||||
DCOS — Distributed Cognitive Operating System
|
||||
A multi-agent cognitive architecture for distributed intelligence.
|
||||
"""
|
||||
|
||||
__version__ = "1.0.0"
|
||||
__author__ = "Celes Renata"
|
||||
|
||||
from .agents.base import Agent, AgentFactory, Spawner, SelfOrganizingMixin, Innovator
|
||||
from .agents.planning import PlanningAgent
|
||||
from .agents.research import ResearchAgent
|
||||
from .agents.logic import LogicAgent
|
||||
from .agents.creative import CreativeAgent
|
||||
from .agents.ethics import EthicsAgent
|
||||
from .agents.simulation import SimulationAgent
|
||||
from .agents.domain import DomainExpertAgent
|
||||
from .communication.protocol import CommunicationProtocol
|
||||
from .communication.network import CommunicationNetwork
|
||||
from .communication.router import MessageRouter
|
||||
from .communication.resolver import AddressResolver
|
||||
from .core.scheduler import TaskScheduler
|
||||
from .core.coordinator import AgentCoordinator
|
||||
from .core.registry import AgentRegistry
|
||||
from .core.allocator import ResourceAllocator
|
||||
from .learning.engine import LearningEngine
|
||||
from .memory.working import WorkingMemory
|
||||
from .memory.conversation import ConversationMemory
|
||||
from .memory.long_term import LongTermMemory
|
||||
from .memory.semantic import SemanticMemory
|
||||
from .memory.episodic import EpisodicMemory
|
||||
from .memory.procedural import ProceduralMemory
|
||||
from .memory.user_model import UserModel
|
||||
from .memory.world_model import WorldModel
|
||||
from .memory.memory_facade import MemoryFacade
|
||||
from .protocols.user_interface import UserInterface
|
||||
from .protocols.external_tools import ExternalTools
|
||||
from .predict import OutcomePredictor
|
||||
from .risk import RiskAssessor
|
||||
from .scenarios import ScenarioEngine
|
||||
from .utils.config import ConfigManager
|
||||
from .utils.cli import main as cli_main
|
||||
|
||||
__all__ = [
|
||||
"Agent", "AgentFactory", "Spawner", "SelfOrganizingMixin", "Innovator",
|
||||
"PlanningAgent", "ResearchAgent", "LogicAgent", "CreativeAgent",
|
||||
"EthicsAgent", "SimulationAgent", "DomainExpertAgent",
|
||||
"CommunicationProtocol", "CommunicationNetwork", "MessageRouter", "AddressResolver",
|
||||
"TaskScheduler", "AgentCoordinator", "AgentRegistry", "ResourceAllocator",
|
||||
"LearningEngine",
|
||||
"WorkingMemory", "ConversationMemory", "LongTermMemory",
|
||||
"SemanticMemory", "EpisodicMemory", "ProceduralMemory",
|
||||
"UserModel", "WorldModel", "MemoryFacade",
|
||||
"UserInterface", "ExternalTools",
|
||||
"OutcomePredictor", "RiskAssessor", "ScenarioEngine",
|
||||
"ConfigManager", "cli_main",
|
||||
]
|
||||
@@ -1,9 +0,0 @@
|
||||
"""
|
||||
DCOS entry point — invoked via `python -m dcos`.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from .utils.cli import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -1,18 +0,0 @@
|
||||
"""
|
||||
Agent subsystem — provides base agent abstractions and specialized agent types.
|
||||
"""
|
||||
|
||||
from .base import Agent, AgentFactory, Spawner, SelfOrganizingMixin, Innovator
|
||||
from .planning import PlanningAgent
|
||||
from .research import ResearchAgent
|
||||
from .logic import LogicAgent
|
||||
from .creative import CreativeAgent
|
||||
from .ethics import EthicsAgent
|
||||
from .simulation import SimulationAgent
|
||||
from .domain import DomainExpertAgent
|
||||
|
||||
__all__ = [
|
||||
"Agent", "AgentFactory", "Spawner", "SelfOrganizingMixin", "Innovator",
|
||||
"PlanningAgent", "ResearchAgent", "LogicAgent", "CreativeAgent",
|
||||
"EthicsAgent", "SimulationAgent", "DomainExpertAgent",
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,136 +0,0 @@
|
||||
"""
|
||||
Base agent classes for the DCOS multi-agent architecture.
|
||||
Provides the foundation for all specialized agent types.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import Any, Dict, List, Optional, Type
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from uuid import uuid4
|
||||
|
||||
|
||||
class AgentRole(Enum):
|
||||
PLANNER = "planner"
|
||||
RESEARCHER = "researcher"
|
||||
LOGICIAN = "logician"
|
||||
CREATIVE = "creative"
|
||||
ETHICIST = "ethicist"
|
||||
SIMULATOR = "simulator"
|
||||
DOMAIN_EXPERT = "domain_expert"
|
||||
|
||||
|
||||
@dataclass
|
||||
class AgentIdentity:
|
||||
id: str = field(default_factory=lambda: uuid4().hex[:16])
|
||||
name: str = ""
|
||||
role: AgentRole = AgentRole.PLANNER
|
||||
version: str = "1.0.0"
|
||||
|
||||
|
||||
class Agent:
|
||||
"""
|
||||
Base class for all DCOS agents.
|
||||
Provides identity, state management, and communication primitives.
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, role: AgentRole = AgentRole.PLANNER) -> None:
|
||||
self.identity = AgentIdentity(name=name, role=role)
|
||||
self._state: Dict[str, Any] = {}
|
||||
self._capabilities: List[str] = []
|
||||
self._peers: Dict[str, "Agent"] = {}
|
||||
|
||||
def register_capability(self, capability: str) -> None:
|
||||
self._capabilities.append(capability)
|
||||
|
||||
def get_capabilities(self) -> List[str]:
|
||||
return list(self._capabilities)
|
||||
|
||||
def set_state(self, key: str, value: Any) -> None:
|
||||
self._state[key] = value
|
||||
|
||||
def get_state(self, key: str) -> Optional[Any]:
|
||||
return self._state.get(key)
|
||||
|
||||
def connect_to(self, peer: "Agent") -> None:
|
||||
self._peers[peer.identity.id] = peer
|
||||
|
||||
def send_message(self, recipient_id: str, message: Any) -> bool:
|
||||
if recipient_id in self._peers:
|
||||
return True
|
||||
return False
|
||||
|
||||
def receive_message(self, sender_id: str, message: Any) -> None:
|
||||
pass
|
||||
|
||||
def act(self, context: Dict[str, Any]) -> Any:
|
||||
"""Perform the agent's primary action based on context."""
|
||||
raise NotImplementedError("Subclasses must implement act()")
|
||||
|
||||
|
||||
class AgentFactory:
|
||||
"""Factory for creating agents with dependency injection."""
|
||||
|
||||
_registry: Dict[str, Type[Agent]] = {}
|
||||
|
||||
@classmethod
|
||||
def register(cls, name: str, agent_class: Type[Agent]) -> None:
|
||||
cls._registry[name] = agent_class
|
||||
|
||||
@classmethod
|
||||
def create(cls, name: str, role: AgentRole, **kwargs: Any) -> Agent:
|
||||
agent_class = cls._registry.get(name, Agent)
|
||||
return agent_class(name=name, role=role, **kwargs)
|
||||
|
||||
|
||||
class Spawner:
|
||||
"""Manages agent lifecycle — spawn, suspend, resume, terminate."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._agents: Dict[str, Agent] = {}
|
||||
self._max_agents: int = 100
|
||||
|
||||
def spawn(self, agent: Agent) -> str:
|
||||
if len(self._agents) >= self._max_agents:
|
||||
raise RuntimeError("Agent pool full")
|
||||
self._agents[agent.identity.id] = agent
|
||||
return agent.identity.id
|
||||
|
||||
def terminate(self, agent_id: str) -> bool:
|
||||
return agent_id in self._agents and bool(self._agents.pop(agent_id, None))
|
||||
|
||||
def get_active_agents(self) -> List[Agent]:
|
||||
return list(self._agents.values())
|
||||
|
||||
|
||||
class SelfOrganizingMixin:
|
||||
"""Mixin for agents that can self-organize into hierarchies."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._parent: Optional[Agent] = None
|
||||
self._children: Dict[str, Agent] = {}
|
||||
|
||||
def adopt(self, child: Agent) -> None:
|
||||
self._children[child.identity.id] = child
|
||||
|
||||
def delegate(self, task: Any) -> Optional[Agent]:
|
||||
if not self._children:
|
||||
return None
|
||||
return next(iter(self._children.values()))
|
||||
|
||||
|
||||
class Innovator:
|
||||
"""Capability for generating novel solutions."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._innovation_history: List[Dict[str, Any]] = []
|
||||
|
||||
def innovate(self, problem: Dict[str, Any]) -> Dict[str, Any]:
|
||||
solution = {
|
||||
"problem": problem,
|
||||
"approach": "cross-domain_synthesis",
|
||||
"novelty_score": 0.7,
|
||||
"solution": "generated_novel_solution",
|
||||
}
|
||||
self._innovation_history.append(solution)
|
||||
return solution
|
||||
@@ -1,32 +0,0 @@
|
||||
"""
|
||||
Creative agent — generates novel ideas, metaphors, and creative solutions.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import Any, Dict, List
|
||||
from .base import Agent, AgentRole
|
||||
|
||||
|
||||
class CreativeAgent(Agent):
|
||||
"""Idea generation and creative problem-solving agent."""
|
||||
|
||||
def __init__(self, name: str = "creative") -> None:
|
||||
super().__init__(name, AgentRole.CREATIVE)
|
||||
self._creations: List[Dict[str, Any]] = []
|
||||
self.register_capability("idea_generation")
|
||||
self.register_capability("analogical_reasoning")
|
||||
|
||||
def act(self, context: Dict[str, Any]) -> Dict[str, Any]:
|
||||
prompt = context.get("prompt", "generate")
|
||||
idea = self.generate(prompt)
|
||||
return {"idea": idea, "originality_score": 0.8}
|
||||
|
||||
def generate(self, prompt: str) -> Dict[str, Any]:
|
||||
idea = {
|
||||
"title": f"Creative concept for: {prompt}",
|
||||
"description": "A novel approach using cross-domain synthesis",
|
||||
"novelty": 0.85,
|
||||
"feasibility": 0.6,
|
||||
}
|
||||
self._creations.append(idea)
|
||||
return idea
|
||||
@@ -1,28 +0,0 @@
|
||||
"""
|
||||
Domain expert agent — provides specialized knowledge in specific domains.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import Any, Dict, List, Optional
|
||||
from .base import Agent, AgentRole
|
||||
|
||||
|
||||
class DomainExpertAgent(Agent):
|
||||
"""Specialized agent with deep knowledge in a particular domain."""
|
||||
|
||||
def __init__(self, name: str = "domain_expert", domain: str = "general") -> None:
|
||||
super().__init__(name, AgentRole.DOMAIN_EXPERT)
|
||||
self.domain = domain
|
||||
self._knowledge_base: Dict[str, Any] = {}
|
||||
self.register_capability(f"domain_knowledge_{domain}")
|
||||
|
||||
def act(self, context: Dict[str, Any]) -> Dict[str, Any]:
|
||||
question = context.get("question", "")
|
||||
answer = self.answer(question)
|
||||
return {"domain": self.domain, "answer": answer}
|
||||
|
||||
def answer(self, question: str) -> str:
|
||||
return f"[{self.domain}] Response to: {question}"
|
||||
|
||||
def add_knowledge(self, key: str, value: Any) -> None:
|
||||
self._knowledge_base[key] = value
|
||||
@@ -1,47 +0,0 @@
|
||||
"""
|
||||
Ethics agent — ensures actions align with ethical guidelines and constraints.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import Any, Dict, List, Tuple
|
||||
from .base import Agent, AgentRole
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class EthicalConstraint(Enum):
|
||||
BENEFICENCE = "beneficence"
|
||||
NON_MALEFICENCE = "non_maleficence"
|
||||
AUTONOMY = "autonomy"
|
||||
JUSTICE = "justice"
|
||||
EXPLICABILITY = "explicability"
|
||||
|
||||
|
||||
class EthicsAgent(Agent):
|
||||
"""Ethical overseer that validates actions against ethical principles."""
|
||||
|
||||
def __init__(self, name: str = "ethicist") -> None:
|
||||
super().__init__(name, AgentRole.ETHICIST)
|
||||
self._constraints: List[EthicalConstraint] = list(EthicalConstraint)
|
||||
self.register_capability("ethical_validation")
|
||||
self.register_capability("value_alignment")
|
||||
|
||||
def act(self, context: Dict[str, Any]) -> Dict[str, Any]:
|
||||
action = context.get("action", "")
|
||||
violations = self.validate(action)
|
||||
return {
|
||||
"action": action,
|
||||
"ethical": len(violations) == 0,
|
||||
"violations": violations,
|
||||
}
|
||||
|
||||
def validate(self, action: str) -> List[str]:
|
||||
violations = []
|
||||
if "deceive" in action.lower():
|
||||
violations.append("autonomy_violation")
|
||||
if "harm" in action.lower():
|
||||
violations.append("non_maleficence_violation")
|
||||
return violations
|
||||
|
||||
def add_constraint(self, constraint: EthicalConstraint) -> None:
|
||||
if constraint not in self._constraints:
|
||||
self._constraints.append(constraint)
|
||||
@@ -1,30 +0,0 @@
|
||||
"""
|
||||
Logic agent — performs reasoning, inference, and logical analysis.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import Any, Dict, List
|
||||
from .base import Agent, AgentRole
|
||||
|
||||
|
||||
class LogicAgent(Agent):
|
||||
"""Deductive and inductive reasoner for logical analysis."""
|
||||
|
||||
def __init__(self, name: str = "logician") -> None:
|
||||
super().__init__(name, AgentRole.LOGICIAN)
|
||||
self._inferences: List[str] = []
|
||||
self.register_capability("logical_reasoning")
|
||||
self.register_capability("contradiction_detection")
|
||||
|
||||
def act(self, context: Dict[str, Any]) -> Dict[str, Any]:
|
||||
premises = context.get("premises", [])
|
||||
conclusion = self.infer(premises)
|
||||
return {"conclusion": conclusion, "valid": True}
|
||||
|
||||
def infer(self, premises: List[str]) -> str:
|
||||
if not premises:
|
||||
return "insufficient_premises"
|
||||
return f"inferred_from_{'_'.join(premises)}"
|
||||
|
||||
def detect_contradiction(self, statements: List[str]) -> bool:
|
||||
return False # Placeholder — real implementation would check logical consistency
|
||||
@@ -1,41 +0,0 @@
|
||||
"""
|
||||
Planning agent — responsible for strategic planning and task decomposition.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import Any, Dict, List
|
||||
from .base import Agent, AgentRole
|
||||
|
||||
|
||||
class PlanningAgent(Agent):
|
||||
"""Strategic planner that decomposes goals into actionable tasks."""
|
||||
|
||||
def __init__(self, name: str = "planner") -> None:
|
||||
super().__init__(name, AgentRole.PLANNER)
|
||||
self._plans: List[Dict[str, Any]] = []
|
||||
self.register_capability("strategic_planning")
|
||||
self.register_capability("task_decomposition")
|
||||
|
||||
def act(self, context: Dict[str, Any]) -> Dict[str, Any]:
|
||||
goal = context.get("goal", "undefined")
|
||||
return {
|
||||
"plan": f"Plan for: {goal}",
|
||||
"steps": [
|
||||
{"step": 1, "action": "analyze", "agent": "researcher"},
|
||||
{"step": 2, "action": "synthesize", "agent": "logician"},
|
||||
{"step": 3, "action": "create", "agent": "creative"},
|
||||
{"step": 4, "action": "validate", "agent": "ethicist"},
|
||||
{"step": 5, "action": "simulate", "agent": "simulator"},
|
||||
],
|
||||
"estimated_duration": 5,
|
||||
}
|
||||
|
||||
def decompose(self, goal: str) -> List[Dict[str, Any]]:
|
||||
tasks = [
|
||||
{"id": "research", "depends_on": []},
|
||||
{"id": "analysis", "depends_on": ["research"]},
|
||||
{"id": "generation", "depends_on": ["analysis"]},
|
||||
{"id": "validation", "depends_on": ["generation"]},
|
||||
]
|
||||
self._plans.append({"goal": goal, "tasks": tasks})
|
||||
return tasks
|
||||
@@ -1,33 +0,0 @@
|
||||
"""
|
||||
Research agent — gathers and synthesizes information from multiple sources.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import Any, Dict, List
|
||||
from .base import Agent, AgentRole
|
||||
|
||||
|
||||
class ResearchAgent(Agent):
|
||||
"""Information gatherer that synthesizes data from multiple sources."""
|
||||
|
||||
def __init__(self, name: str = "researcher") -> None:
|
||||
super().__init__(name, AgentRole.RESEARCHER)
|
||||
self._findings: Dict[str, Any] = {}
|
||||
self.register_capability("information_gathering")
|
||||
self.register_capability("cross_referencing")
|
||||
|
||||
def act(self, context: Dict[str, Any]) -> Dict[str, Any]:
|
||||
query = context.get("query", "general")
|
||||
return {
|
||||
"findings": [f"Result for: {query}"],
|
||||
"confidence": 0.85,
|
||||
"sources": ["internal_knowledge", "external_apis"],
|
||||
}
|
||||
|
||||
def research(self, topic: str, depth: int = 1) -> Dict[str, Any]:
|
||||
return {
|
||||
"topic": topic,
|
||||
"summary": f"Research summary on {topic}",
|
||||
"key_findings": [f"Finding {i} for {topic}" for i in range(depth)],
|
||||
"confidence": 0.75,
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
"""
|
||||
Simulation agent — runs simulations to test scenarios and predict outcomes.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import Any, Dict, List, Optional
|
||||
from .base import Agent, AgentRole
|
||||
|
||||
|
||||
class SimulationAgent(Agent):
|
||||
"""Agent that models and runs simulations of complex systems."""
|
||||
|
||||
def __init__(self, name: str = "simulator") -> None:
|
||||
super().__init__(name, AgentRole.SIMULATOR)
|
||||
self._simulations: List[Dict[str, Any]] = []
|
||||
self.register_capability("scenario_simulation")
|
||||
self.register_capability("monte_carlo_forecasting")
|
||||
|
||||
def act(self, context: Dict[str, Any]) -> Dict[str, Any]:
|
||||
scenario = context.get("scenario", "default")
|
||||
result = self.simulate(scenario)
|
||||
return {"result": result, "iterations": 1000}
|
||||
|
||||
def simulate(self, scenario: str, iterations: int = 100) -> Dict[str, Any]:
|
||||
result = {
|
||||
"scenario": scenario,
|
||||
"success_probability": 0.72,
|
||||
"mean_outcome": "positive",
|
||||
"variance": 0.15,
|
||||
"confidence_interval": (0.65, 0.85),
|
||||
}
|
||||
self._simulations.append(result)
|
||||
return result
|
||||
@@ -1,15 +0,0 @@
|
||||
"""
|
||||
Communication subsystem — provides message passing, routing, and discovery.
|
||||
"""
|
||||
|
||||
from .protocol import CommunicationProtocol
|
||||
from .network import CommunicationNetwork
|
||||
from .router import MessageRouter
|
||||
from .resolver import AddressResolver
|
||||
|
||||
__all__ = [
|
||||
"CommunicationProtocol",
|
||||
"CommunicationNetwork",
|
||||
"MessageRouter",
|
||||
"AddressResolver",
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,46 +0,0 @@
|
||||
"""
|
||||
Communication network — manages agent connectivity and message delivery.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import Any, Dict, List, Optional
|
||||
from .protocol import Message, CommunicationProtocol
|
||||
|
||||
|
||||
class CommunicationNetwork:
|
||||
"""Manages the topology and message delivery between agents."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._protocol = CommunicationProtocol()
|
||||
self._connections: Dict[str, List[str]] = {} # agent_id -> [peer_ids]
|
||||
self._message_log: List[Message] = []
|
||||
|
||||
def connect(self, agent_a: str, agent_b: str) -> None:
|
||||
self._connections.setdefault(agent_a, []).append(agent_b)
|
||||
self._connections.setdefault(agent_b, []).append(agent_a)
|
||||
|
||||
def disconnect(self, agent_a: str, agent_b: str) -> None:
|
||||
for peer in (agent_a, agent_b):
|
||||
if peer in self._connections:
|
||||
self._connections[peer] = [
|
||||
p for p in self._connections[peer] if p != agent_b
|
||||
]
|
||||
|
||||
def deliver(self, message: Message) -> bool:
|
||||
if message.recipient in self._connections.get(message.sender, []):
|
||||
self._message_log.append(message)
|
||||
return self._protocol.send(message)
|
||||
return False
|
||||
|
||||
def broadcast(self, sender: str, payload: Any) -> int:
|
||||
peers = self._connections.get(sender, [])
|
||||
for peer in peers:
|
||||
msg = Message(sender=sender, recipient=peer, payload=payload)
|
||||
self._message_log.append(msg)
|
||||
return len(peers)
|
||||
|
||||
def get_peers(self, agent_id: str) -> List[str]:
|
||||
return self._connections.get(agent_id, [])
|
||||
|
||||
def get_message_log(self, limit: int = 100) -> List[Message]:
|
||||
return self._message_log[-limit:]
|
||||
@@ -1,53 +0,0 @@
|
||||
"""
|
||||
Communication protocol — defines message formats and delivery semantics.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import Any, Dict, Optional
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from uuid import uuid4
|
||||
|
||||
|
||||
@dataclass
|
||||
class Message:
|
||||
sender: str
|
||||
recipient: str
|
||||
payload: Any
|
||||
msg_id: str = field(default_factory=lambda: uuid4().hex)
|
||||
timestamp: datetime = field(default_factory=datetime.now)
|
||||
ttl: int = 60 # seconds
|
||||
priority: int = 0
|
||||
|
||||
|
||||
class CommunicationProtocol:
|
||||
"""Defines the message format, encoding, and delivery guarantees."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._pending: Dict[str, Message] = {}
|
||||
|
||||
def encode(self, message: Message) -> Dict[str, Any]:
|
||||
return {
|
||||
"sender": message.sender,
|
||||
"recipient": message.recipient,
|
||||
"payload": message.payload,
|
||||
"msg_id": message.msg_id,
|
||||
"timestamp": message.timestamp.isoformat(),
|
||||
"ttl": message.ttl,
|
||||
"priority": message.priority,
|
||||
}
|
||||
|
||||
def decode(self, data: Dict[str, Any]) -> Message:
|
||||
return Message(
|
||||
sender=data["sender"],
|
||||
recipient=data["recipient"],
|
||||
payload=data["payload"],
|
||||
msg_id=data.get("msg_id", uuid4().hex),
|
||||
)
|
||||
|
||||
def send(self, message: Message) -> bool:
|
||||
self._pending[message.msg_id] = message
|
||||
return True
|
||||
|
||||
def ack(self, msg_id: str) -> Optional[Message]:
|
||||
return self._pending.pop(msg_id, None)
|
||||
@@ -1,32 +0,0 @@
|
||||
"""
|
||||
Address resolver — resolves agent names/roles to their network addresses.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
|
||||
class AddressResolver:
|
||||
"""Resolves symbolic agent names and roles to concrete agent IDs."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._name_table: Dict[str, str] = {} # name -> agent_id
|
||||
self._role_table: Dict[str, List[str]] = {} # role -> [agent_ids]
|
||||
|
||||
def register(self, name: str, agent_id: str, role: Optional[str] = None) -> None:
|
||||
self._name_table[name] = agent_id
|
||||
if role:
|
||||
self._role_table.setdefault(role, []).append(agent_id)
|
||||
|
||||
def resolve_by_name(self, name: str) -> Optional[str]:
|
||||
return self._name_table.get(name)
|
||||
|
||||
def resolve_by_role(self, role: str) -> List[str]:
|
||||
return self._role_table.get(role, [])
|
||||
|
||||
def unregister(self, name: str) -> Optional[str]:
|
||||
agent_id = self._name_table.pop(name, None)
|
||||
if agent_id:
|
||||
for role, ids in self._role_table.items():
|
||||
self._role_table[role] = [i for i in ids if i != agent_id]
|
||||
return agent_id
|
||||
@@ -1,34 +0,0 @@
|
||||
"""
|
||||
Message router — directs messages to the correct recipient based on routing rules.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import Any, Dict, List, Optional
|
||||
from .protocol import Message
|
||||
|
||||
|
||||
class MessageRouter:
|
||||
"""Routes messages between agents based on content, role, or address."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._routes: Dict[str, str] = {} # routing_key -> agent_id
|
||||
self._fallback: Optional[str] = None
|
||||
|
||||
def register(self, routing_key: str, agent_id: str) -> None:
|
||||
self._routes[routing_key] = agent_id
|
||||
|
||||
def unregister(self, routing_key: str) -> Optional[str]:
|
||||
return self._routes.pop(routing_key, None)
|
||||
|
||||
def route(self, message: Message) -> Optional[str]:
|
||||
payload_str = str(message.payload)
|
||||
for key, agent_id in self._routes.items():
|
||||
if key in payload_str:
|
||||
return agent_id
|
||||
return self._fallback
|
||||
|
||||
def set_fallback(self, agent_id: str) -> None:
|
||||
self._fallback = agent_id
|
||||
|
||||
def get_registered_routes(self) -> Dict[str, str]:
|
||||
return dict(self._routes)
|
||||
@@ -1,15 +0,0 @@
|
||||
"""
|
||||
Core subsystem — scheduling, coordination, registry, and resource allocation.
|
||||
"""
|
||||
|
||||
from .scheduler import TaskScheduler
|
||||
from .coordinator import AgentCoordinator
|
||||
from .registry import AgentRegistry
|
||||
from .allocator import ResourceAllocator
|
||||
|
||||
__all__ = [
|
||||
"TaskScheduler",
|
||||
"AgentCoordinator",
|
||||
"AgentRegistry",
|
||||
"ResourceAllocator",
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,45 +0,0 @@
|
||||
"""
|
||||
Resource allocator — manages compute and memory resource distribution among agents.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
|
||||
class ResourceAllocator:
|
||||
"""Allocates system resources (compute, memory, bandwidth) among agents."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._total_cpu: float = 100.0
|
||||
self._total_memory: float = 1024.0
|
||||
self._allocations: Dict[str, Dict[str, float]] = {}
|
||||
|
||||
def allocate(self, agent_id: str, cpu: float, memory: float) -> bool:
|
||||
if cpu <= 0 or memory <= 0:
|
||||
return False
|
||||
remaining_cpu = self._total_cpu - sum(
|
||||
a.get("cpu", 0) for a in self._allocations.values()
|
||||
)
|
||||
remaining_memory = self._total_memory - sum(
|
||||
a.get("memory", 0) for a in self._allocations.values()
|
||||
)
|
||||
if cpu > remaining_cpu or memory > remaining_memory:
|
||||
return False
|
||||
self._allocations[agent_id] = {"cpu": cpu, "memory": memory}
|
||||
return True
|
||||
|
||||
def release(self, agent_id: str) -> bool:
|
||||
return agent_id in self._allocations and bool(
|
||||
self._allocations.pop(agent_id, None)
|
||||
)
|
||||
|
||||
def get_allocation(self, agent_id: str) -> Dict[str, float]:
|
||||
return self._allocations.get(agent_id, {"cpu": 0.0, "memory": 0.0})
|
||||
|
||||
def utilization(self) -> Dict[str, float]:
|
||||
total_cpu = sum(a.get("cpu", 0) for a in self._allocations.values())
|
||||
total_mem = sum(a.get("memory", 0) for a in self._allocations.values())
|
||||
return {
|
||||
"cpu": total_cpu / self._total_cpu,
|
||||
"memory": total_mem / self._total_memory,
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
"""
|
||||
Agent coordinator — orchestrates multi-agent workflows and collaboration.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import Any, Dict, List, Optional
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class AgentCoordinator:
|
||||
"""Coordinates multiple agents to work on complex tasks collaboratively."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._workflows: Dict[str, List[str]] = {} # workflow_id -> [agent_ids]
|
||||
self._active: Dict[str, Dict[str, Any]] = {}
|
||||
|
||||
def create_workflow(self, workflow_id: str, agent_ids: List[str]) -> None:
|
||||
self._workflows[workflow_id] = agent_ids
|
||||
self._active[workflow_id] = {
|
||||
"status": "created",
|
||||
"created_at": datetime.now().isoformat(),
|
||||
}
|
||||
|
||||
def assign_task(self, workflow_id: str, agent_id: str, task: Any) -> bool:
|
||||
workflow = self._workflows.get(workflow_id)
|
||||
if workflow and agent_id in workflow:
|
||||
self._active.setdefault(workflow_id, {})["current_task"] = str(task)
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_workflow_status(self, workflow_id: str) -> Optional[Dict[str, Any]]:
|
||||
return self._active.get(workflow_id)
|
||||
|
||||
def complete_workflow(self, workflow_id: str) -> bool:
|
||||
if workflow_id in self._active:
|
||||
self._active[workflow_id]["status"] = "completed"
|
||||
return True
|
||||
return False
|
||||
@@ -1,48 +0,0 @@
|
||||
"""
|
||||
Agent registry — maintains a directory of all active agents and their capabilities.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import Any, Dict, List, Optional
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
|
||||
@dataclass
|
||||
class AgentRecord:
|
||||
agent_id: str
|
||||
name: str
|
||||
role: str
|
||||
capabilities: List[str] = field(default_factory=list)
|
||||
status: str = "active"
|
||||
address: str = ""
|
||||
|
||||
|
||||
class AgentRegistry:
|
||||
"""Directory of all agents in the system with their metadata."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._agents: Dict[str, AgentRecord] = {}
|
||||
|
||||
def register(self, record: AgentRecord) -> None:
|
||||
self._agents[record.agent_id] = record
|
||||
|
||||
def unregister(self, agent_id: str) -> Optional[AgentRecord]:
|
||||
return self._agents.pop(agent_id, None)
|
||||
|
||||
def get(self, agent_id: str) -> Optional[AgentRecord]:
|
||||
return self._agents.get(agent_id)
|
||||
|
||||
def find_by_role(self, role: str) -> List[AgentRecord]:
|
||||
return [a for a in self._agents.values() if a.role == role]
|
||||
|
||||
def find_by_capability(self, capability: str) -> List[AgentRecord]:
|
||||
return [
|
||||
a for a in self._agents.values()
|
||||
if capability in a.capabilities
|
||||
]
|
||||
|
||||
def list_active(self) -> List[AgentRecord]:
|
||||
return [a for a in self._agents.values() if a.status == "active"]
|
||||
|
||||
def count(self) -> int:
|
||||
return len(self._agents)
|
||||
@@ -1,81 +0,0 @@
|
||||
"""
|
||||
Task scheduler — manages task queuing, prioritization, and execution ordering.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import Any, Dict, List, Optional
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from uuid import uuid4
|
||||
|
||||
|
||||
class TaskStatus(Enum):
|
||||
PENDING = "pending"
|
||||
RUNNING = "running"
|
||||
COMPLETED = "completed"
|
||||
FAILED = "failed"
|
||||
|
||||
|
||||
@dataclass
|
||||
class Task:
|
||||
id: str = field(default_factory=lambda: uuid4().hex)
|
||||
name: str = ""
|
||||
agent_id: str = ""
|
||||
status: TaskStatus = TaskStatus.PENDING
|
||||
priority: int = 0
|
||||
created_at: datetime = field(default_factory=datetime.now)
|
||||
depends_on: List[str] = field(default_factory=list)
|
||||
|
||||
|
||||
class TaskScheduler:
|
||||
"""Schedules and dispatches tasks to agents."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._queue: List[Task] = []
|
||||
self._running: List[Task] = []
|
||||
self._completed: List[Task] = []
|
||||
|
||||
def enqueue(self, task: Task) -> None:
|
||||
self._queue.append(task)
|
||||
self._queue.sort(key=lambda t: t.priority, reverse=True)
|
||||
|
||||
def dequeue(self) -> Optional[Task]:
|
||||
if not self._queue:
|
||||
return None
|
||||
# Find first task whose dependencies are met
|
||||
for task in self._queue:
|
||||
deps_met = all(
|
||||
dep in [c.id for c in self._completed]
|
||||
for dep in task.depends_on
|
||||
)
|
||||
if deps_met:
|
||||
self._queue.remove(task)
|
||||
task.status = TaskStatus.RUNNING
|
||||
self._running.append(task)
|
||||
return task
|
||||
return None
|
||||
|
||||
def complete(self, task_id: str) -> bool:
|
||||
for task in self._running:
|
||||
if task.id == task_id:
|
||||
self._running.remove(task)
|
||||
task.status = TaskStatus.COMPLETED
|
||||
self._completed.append(task)
|
||||
return True
|
||||
return False
|
||||
|
||||
def fail(self, task_id: str) -> bool:
|
||||
for task in self._running:
|
||||
if task.id == task_id:
|
||||
self._running.remove(task)
|
||||
task.status = TaskStatus.FAILED
|
||||
self._completed.append(task)
|
||||
return True
|
||||
return False
|
||||
|
||||
def pending_count(self) -> int:
|
||||
return len(self._queue)
|
||||
|
||||
def running_count(self) -> int:
|
||||
return len(self._running)
|
||||
@@ -1,7 +0,0 @@
|
||||
"""
|
||||
Learning subsystem — provides reinforcement learning and model updates.
|
||||
"""
|
||||
|
||||
from .engine import LearningEngine
|
||||
|
||||
__all__ = ["LearningEngine"]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,44 +0,0 @@
|
||||
"""
|
||||
Learning engine — reinforcement learning and model updates for agents.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import Any, Dict, List, Optional
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
|
||||
@dataclass
|
||||
class Experience:
|
||||
state: Dict[str, Any]
|
||||
action: str
|
||||
reward: float
|
||||
next_state: Optional[Dict[str, Any]] = None
|
||||
|
||||
|
||||
class LearningEngine:
|
||||
"""Reinforcement learning engine that improves agent behavior over time."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._experiences: List[Experience] = []
|
||||
self._policy: Dict[str, float] = {}
|
||||
|
||||
def record_experience(self, exp: Experience) -> None:
|
||||
self._experiences.append(exp)
|
||||
self._update_policy(exp)
|
||||
|
||||
def _update_policy(self, exp: Experience) -> None:
|
||||
current = self._policy.get(exp.action, 0.0)
|
||||
self._policy[exp.action] = current + exp.reward * 0.1
|
||||
|
||||
def get_action_score(self, action: str) -> float:
|
||||
return self._policy.get(action, 0.0)
|
||||
|
||||
def best_action(self, actions: List[str]) -> Optional[str]:
|
||||
if not actions:
|
||||
return None
|
||||
scored = [(a, self._policy.get(a, 0.0)) for a in actions]
|
||||
scored.sort(key=lambda x: x[1], reverse=True)
|
||||
return scored[0][0]
|
||||
|
||||
def experience_count(self) -> int:
|
||||
return len(self._experiences)
|
||||
@@ -1,25 +0,0 @@
|
||||
"""
|
||||
Memory subsystem — multiple memory types and the unified facade.
|
||||
"""
|
||||
|
||||
from .working import WorkingMemory
|
||||
from .conversation import ConversationMemory
|
||||
from .long_term import LongTermMemory
|
||||
from .semantic import SemanticMemory
|
||||
from .episodic import EpisodicMemory
|
||||
from .procedural import ProceduralMemory
|
||||
from .user_model import UserModel
|
||||
from .world_model import WorldModel
|
||||
from .memory_facade import MemoryFacade
|
||||
|
||||
__all__ = [
|
||||
"WorkingMemory",
|
||||
"ConversationMemory",
|
||||
"LongTermMemory",
|
||||
"SemanticMemory",
|
||||
"EpisodicMemory",
|
||||
"ProceduralMemory",
|
||||
"UserModel",
|
||||
"WorldModel",
|
||||
"MemoryFacade",
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user