File size: 7,648 Bytes
faff4ab
 
 
 
 
 
 
5f7e99a
 
 
 
9349c78
 
5f7e99a
 
 
 
 
 
 
 
 
d2cc510
bd9a8ac
 
5f7e99a
 
 
2bd7ac2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5f7e99a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cc69dae
 
b93ff7c
cc69dae
b93ff7c
cc69dae
 
b93ff7c
cc69dae
b93ff7c
cc69dae
 
 
 
b93ff7c
cc69dae
387e8e2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5f7e99a
 
 
 
 
 
 
 
 
 
 
 
bd9a8ac
 
cc69dae
5f7e99a
 
387e8e2
 
5f7e99a
f2e2f17
 
 
dc7b2c9
f2e2f17
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
---
license: mit
tags:
    - noesis
    - stateful-agent
    - custom-model-format
---
# Core Model

`me.py` implements a small stateful AI model. It combines a feed-forward neural network with state, goals, memory, rules, and a behavior program. It uses only the Python standard library.

Live Preview: [noesis.dodle.link/model](https://noesis.dodle.link/model/)

## Create a model file

Run the script from the `core/model/` directory:

```bash
cd core/model
python3 me.py
```

This creates `noe-model.dodl` and `noe-model.F32.gguf` in the current directory. The `F32` suffix identifies the GGUF file's float32 tensor type. The DODL file stores the complete model, including its state, memory, rules, behavior, and network. The GGUF file contains the network's four float32 tensors and architecture dimensions; it uses the custom `noesis` architecture and requires a compatible runtime to execute.

`create_model_file(path)` is also available when creating a model from Python; it writes the binary DODL format and returns the model object. Pass that model to `create_gguf_model_file(path, model)` to write the matching GGUF file.

The path may be a string or another path-like value. Its parent directory must already exist. The file is binary; open it with `"rb"` when reading.

## Train the pixel controller

The pixel demo uses the same DODL container with a controller-specific network: eight normalized inputs (pixel position and velocity, pointer offset and presence, and excitement) and two steering outputs. Train it from `core/model/`:

```bash
python3 me.py --train-pixel
```

This writes `noe-pixel-model.dodl` without replacing the general-purpose `noe-model.dodl`. Training is supervised by a built-in steering policy that moves away from a nearby pointer and screen edges. `--samples`, `--epochs`, `--seed`, and `--output` can be supplied to adjust training, choose another output path, and make training repeatable. The browser runs inference only; it does not update the model.

Serve `core/model/` over HTTP so the browser can fetch the binary model:

```bash
python3 -m http.server 8000
```

Then open `http://localhost:8000/`. `index.html` loads `me.js`, which fetches `noe-pixel-model.dodl`; if the file is missing or is not a pixel model, the demo falls back to its built-in steering behavior.

## Load and use a model

Deserialize the file into a model, then pass it to `AIEngine`:

```python
from model.me import AIEngine, deserialize_model

with open("noe-model.dodl", "rb") as model_file:
    model = deserialize_model(model_file.read())

engine = AIEngine(model)
result = engine.step({"type": "user_input", "value": "hello"})
print(result["action"])
```

Alternatively, `AIEngine.import_model(data)` constructs an engine directly from serialized bytes. `AIEngine.export_model()` returns the engine's current model as serialized bytes; write those bytes to a file to persist the latest state:

```python
with open("noe-model.dodl", "wb") as model_file:
    model_file.write(engine.export_model())
```

## Merge compatible models

`merge_models(model_a, model_b, alpha=0.5)` interpolates corresponding network parameters. `alpha=0` keeps model A's parameters, `alpha=1` uses model B's, and values in between blend the two. Model B defines the merged network architecture; when dimensions differ, overlapping parameters from model A are mapped by index and any additional dimensions retain model B's parameters. The merged model keeps model A's state, rules, memory, behavior, and goals, while receiving a new ID and timestamps.

Pass DODL model dictionaries or supported model-file paths, merge them, then serialize the result:

```python
from model.me import merge_models, serialize_model

merged = merge_models("model-a.dodl", "model-b.onnx", alpha=0.5)
with open("merged-model.dodl", "wb") as model_file:
    model_file.write(serialize_model(merged))
```

ONNX loading requires `pip install onnx` and supports a graph consisting of `Gemm → Relu → Gemm` with weights and biases stored as initializers. The imported ONNX dimensions are preserved in the merged DODL model. This merges weights and biases only; it does not combine memories, rules, or other model metadata. Direct parameter averaging may work poorly for independently trained networks, even when their shapes match.

## Generate a reply with OpenAI or Gemini

`LLMProvider` sends the input, current state, and up to five recent memories to OpenAI or Gemini. It uses Python's standard library, so no provider SDK is required. Set the provider and its API key in the environment; credentials are never stored in the DODL file:

```bash
export NOESIS_PROVIDER=openai  # or gemini
export OPENAI_API_KEY=your-api-key
```

For Gemini, set `GEMINI_API_KEY` instead. `NOESIS_PROVIDER` defaults to `openai`. The default models are `gpt-4o-mini` and `gemini-2.0-flash`; override them with `OPENAI_MODEL` or `GEMINI_MODEL` if needed.

```python
from model.me import AIEngine, LLMProvider

with open("noe-model.dodl", "rb") as model_file:
    engine = AIEngine.import_model(model_file.read())

provider = LLMProvider()  # Reads NOESIS_PROVIDER and the matching API key
result = engine.respond({"type": "user_input", "value": "Hello!"}, provider)
print(result["reply"])

with open("noe-model.dodl", "wb") as model_file:
    model_file.write(engine.export_model())
```

`respond()` asks the provider for a text reply, then runs one normal local engine step and returns its result with a `reply` field. The provider generates the conversational reply; it does not choose the engine's action. Provider configuration is runtime-only and does not change the DODL format. `LLMProviderError` is raised for provider request or response errors.

## Model format and limits

The file starts with the `DODL` magic signature and a versioned header. It stores JSON metadata followed by the neural network's float32 arrays. The format is intended for this module's serializer and deserializer; it is not a JSON document.

`serialize_model()` validates the memory, rule, and behavior limits and rejects files larger than 1 MiB. `deserialize_model()` checks the signature, format version, network shape, and encoded size before returning a model.

## Main API

- `create_model()` returns a fresh model in memory.
- `create_model_file(path)` creates a fresh model and writes it to `path`.
- `serialize_model(model)` converts a model to DODL bytes.
- `deserialize_model(data)` converts DODL bytes back to a model.
- `serialize_gguf_model(model)` converts the network tensors to GGUF v3 bytes.
- `create_gguf_model_file(path, model)` writes those GGUF bytes to `path`.
- `merge_models(model_a, model_b, alpha=0.5)` blends compatible models' network parameters.
- `AIEngine(model=None)` runs and updates a model; without an argument it creates a fresh one.
- `AIEngine.step(input_value)` observes input, chooses and executes an action, evaluates it, learns, updates state, and returns the cycle result.
- `LLMProvider(provider=None)` configures an OpenAI or Gemini text-generation adapter from environment variables.
- `AIEngine.respond(input_value, provider=None)` generates a provider reply and runs one local engine step.
- `AIEngine.export_model()` serializes the engine's current model.

## License

Noesis is released under the [MIT License](https://huggingface.co/dodle/noesis/blob/main/LICENSE). Individual components
also include their own license files where applicable.

## Contributing

To contribute, create a focused change in the relevant component, update its
documentation when behavior changes, and run that component's existing checks
before opening a pull request.