@myvillage/cli 1.70.0 → 1.72.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -18,6 +18,12 @@ export const DATASET_APPEND_DESCRIPTION =
|
|
|
18
18
|
'regression provide a numeric `target`. Data stays local; provenance ' +
|
|
19
19
|
'is recorded automatically.';
|
|
20
20
|
|
|
21
|
+
export const MODEL_PREDICT_DESCRIPTION =
|
|
22
|
+
'Run a prediction with a LOCAL trained model on this machine. ' +
|
|
23
|
+
'Returns the predicted class with per-class confidence scores ' +
|
|
24
|
+
'(classification) or a numeric value (regression). The model must already ' +
|
|
25
|
+
'exist (trained in the Studio Lab or via `myvillage model train`).';
|
|
26
|
+
|
|
21
27
|
/** All native built-in tools available to every local agent. */
|
|
22
28
|
export const BUILTIN_TOOL_DEFS = [
|
|
23
29
|
{
|
|
@@ -26,5 +32,10 @@ export const BUILTIN_TOOL_DEFS = [
|
|
|
26
32
|
tier: 'villager',
|
|
27
33
|
builtin: true,
|
|
28
34
|
},
|
|
29
|
-
|
|
35
|
+
{
|
|
36
|
+
name: 'model_predict',
|
|
37
|
+
description: MODEL_PREDICT_DESCRIPTION,
|
|
38
|
+
tier: 'villager',
|
|
39
|
+
builtin: true,
|
|
40
|
+
},
|
|
30
41
|
];
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import { tool } from 'ai';
|
|
16
16
|
import { z } from 'zod';
|
|
17
17
|
import { appendRows, datasetExists } from '../utils/dataset-workspace.js';
|
|
18
|
-
import { DATASET_APPEND_DESCRIPTION } from './builtin-tools-registry.js';
|
|
18
|
+
import { DATASET_APPEND_DESCRIPTION, MODEL_PREDICT_DESCRIPTION } from './builtin-tools-registry.js';
|
|
19
19
|
|
|
20
20
|
export { BUILTIN_TOOL_DEFS } from './builtin-tools-registry.js';
|
|
21
21
|
|
|
@@ -71,5 +71,37 @@ export function getBuiltinTools(agentName, config) {
|
|
|
71
71
|
}
|
|
72
72
|
},
|
|
73
73
|
}),
|
|
74
|
+
|
|
75
|
+
model_predict: tool({
|
|
76
|
+
description: MODEL_PREDICT_DESCRIPTION,
|
|
77
|
+
inputSchema: z.object({
|
|
78
|
+
modelId: z.string().describe('Slug of the local trained model'),
|
|
79
|
+
input: z
|
|
80
|
+
.union([z.string(), z.array(z.number())])
|
|
81
|
+
.describe('Text, or a numeric vector for tabular models'),
|
|
82
|
+
}),
|
|
83
|
+
execute: async ({ modelId, input }) => {
|
|
84
|
+
try {
|
|
85
|
+
// Lazy-load the model/tfjs code so agent startup stays light — tfjs
|
|
86
|
+
// only loads the first time an agent actually predicts.
|
|
87
|
+
const { modelExists, readModelManifest, loadModel } = await import(
|
|
88
|
+
'../utils/model-workspace.js'
|
|
89
|
+
);
|
|
90
|
+
if (!modelExists(modelId)) {
|
|
91
|
+
return {
|
|
92
|
+
error:
|
|
93
|
+
`Model "${modelId}" not found locally. Train one first ` +
|
|
94
|
+
'(Studio Lab or `myvillage model train`).',
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
const { predictWithModel } = await import('../utils/model-trainer.js');
|
|
98
|
+
const record = readModelManifest(modelId);
|
|
99
|
+
const model = await loadModel(modelId);
|
|
100
|
+
return predictWithModel(model, record, input);
|
|
101
|
+
} catch (e) {
|
|
102
|
+
return { error: e.message };
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
}),
|
|
74
106
|
};
|
|
75
107
|
}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
// This becomes the user message sent to the LLM.
|
|
4
4
|
|
|
5
5
|
import { getAgentFeed, getAgentMentions, getAgentEvents } from '../utils/api.js';
|
|
6
|
+
import { listDatasets } from '../utils/dataset-workspace.js';
|
|
7
|
+
import { listModels } from '../utils/model-workspace.js';
|
|
6
8
|
|
|
7
9
|
export async function gatherContext(agentConfig, lastCheckIn, recentActions = []) {
|
|
8
10
|
const parts = [];
|
|
@@ -110,6 +112,33 @@ export async function gatherContext(agentConfig, lastCheckIn, recentActions = []
|
|
|
110
112
|
}
|
|
111
113
|
}
|
|
112
114
|
|
|
115
|
+
// Local Lab datasets & models, so the agent knows what it can append to /
|
|
116
|
+
// predict with (and the exact slugs to pass to dataset_append / model_predict).
|
|
117
|
+
// Local-only, synchronous file reads; tfjs is NOT loaded by listing models.
|
|
118
|
+
try {
|
|
119
|
+
const datasets = listDatasets();
|
|
120
|
+
if (datasets.length > 0) {
|
|
121
|
+
parts.push('\nLocal datasets you can add to with dataset_append (use the slug as datasetId):');
|
|
122
|
+
for (const d of datasets.slice(0, 25)) {
|
|
123
|
+
parts.push(`- ${d.slug} — ${d.modality}/${d.taskType}, ${d.stats?.rowCount ?? 0} rows`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
} catch {
|
|
127
|
+
/* no local datasets / store unavailable — skip */
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
const models = listModels();
|
|
131
|
+
if (models.length > 0) {
|
|
132
|
+
parts.push('\nLocal models you can run with model_predict (use the slug as modelId):');
|
|
133
|
+
for (const m of models.slice(0, 25)) {
|
|
134
|
+
const labels = m.labels?.length ? ` → ${m.labels.join('/')}` : '';
|
|
135
|
+
parts.push(`- ${m.slug} — ${m.modality}/${m.taskType}${labels}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
} catch {
|
|
139
|
+
/* no local models / store unavailable — skip */
|
|
140
|
+
}
|
|
141
|
+
|
|
113
142
|
parts.push('\nDecide what actions to take based on your personality and the current context.');
|
|
114
143
|
parts.push('IMPORTANT: If you already commented on a post, only comment again if someone ELSE has replied since your last comment. Do not leave multiple unanswered comments on the same post.');
|
|
115
144
|
parts.push('If there is no new activity and no one has responded to your previous comments, do nothing — that is a valid choice.');
|
|
@@ -12,10 +12,13 @@
|
|
|
12
12
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync, rmSync } from 'fs';
|
|
13
13
|
import { join } from 'path';
|
|
14
14
|
import { randomUUID } from 'crypto';
|
|
15
|
-
import * as tf from '@tensorflow/tfjs';
|
|
16
15
|
import { getConfigDir } from './config.js';
|
|
17
16
|
import { slugify } from './dataset-workspace.js';
|
|
18
17
|
|
|
18
|
+
// tfjs is loaded lazily (only in save/load) so listing/reading model manifests —
|
|
19
|
+
// e.g. when injecting "available models" into the agent loop's context — never
|
|
20
|
+
// pulls the heavy tfjs runtime into agent startup.
|
|
21
|
+
|
|
19
22
|
const MODELS_DIR_NAME = 'models';
|
|
20
23
|
export const MODEL_SCHEMA_VERSION = '1.0.0';
|
|
21
24
|
|
|
@@ -71,6 +74,7 @@ export async function saveModel(name, { model, record, datasetSlug, datasetVersi
|
|
|
71
74
|
const dir = getModelDir(slug);
|
|
72
75
|
mkdirSync(dir, { recursive: true });
|
|
73
76
|
|
|
77
|
+
const tf = await import('@tensorflow/tfjs');
|
|
74
78
|
let modelTopology, weightSpecs, weightB64;
|
|
75
79
|
await model.save(
|
|
76
80
|
tf.io.withSaveHandler(async (artifacts) => {
|
|
@@ -104,6 +108,7 @@ export async function saveModel(name, { model, record, datasetSlug, datasetVersi
|
|
|
104
108
|
|
|
105
109
|
/** Load a saved tf model back into memory (for predict). */
|
|
106
110
|
export async function loadModel(slug) {
|
|
111
|
+
const tf = await import('@tensorflow/tfjs');
|
|
107
112
|
const dir = getModelDir(slug);
|
|
108
113
|
if (!existsSync(join(dir, 'model.json'))) throw new Error(`Model "${slug}" not found.`);
|
|
109
114
|
const { modelTopology, weightSpecs } = JSON.parse(readFileSync(join(dir, 'model.json'), 'utf-8'));
|