alchemy 0.0.8 → 0.1.1
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 +23 -22
- package/src/alchemize.ts +151 -0
- package/src/apply.ts +117 -0
- package/src/components/agent/agent.ts +45 -0
- package/src/components/agent/anthropic.ts +27 -0
- package/src/components/agent/index.ts +4 -0
- package/src/components/agent/model.ts +22 -0
- package/src/components/agent/openai.ts +44 -0
- package/src/components/agent/package.ts +126 -0
- package/src/components/agent/requirements.ts +111 -0
- package/src/components/agent/scrape.ts +47 -0
- package/src/components/agent/tsconfig.ts +150 -0
- package/src/components/agent/typescript.ts +177 -0
- package/src/components/aws/bucket.ts +168 -0
- package/src/components/aws/function.ts +302 -0
- package/src/components/aws/index.ts +6 -0
- package/src/components/aws/policy.ts +245 -0
- package/src/components/aws/queue.ts +222 -0
- package/src/components/aws/role.ts +245 -0
- package/src/components/aws/table.ts +172 -0
- package/src/components/esbuild.ts +128 -0
- package/src/components/fs.ts +45 -0
- package/src/destroy.ts +63 -0
- package/src/error.ts +16 -0
- package/src/global.ts +72 -0
- package/src/index.ts +6 -0
- package/src/input.ts +16 -0
- package/src/main.ts +34 -0
- package/src/output.ts +49 -0
- package/src/resource.ts +313 -0
- package/src/state.ts +100 -0
- package/test/aws/function.test.ts +144 -0
- package/test/aws/queue.test.ts +85 -0
- package/test/aws/role.test.ts +153 -0
- package/test/aws/table.test.ts +73 -0
- package/test/esbuild.test.ts +41 -0
- package/test/handler.ts +9 -0
- package/tsconfig.json +7 -0
- package/Column.js +0 -1
- package/Create.js +0 -1
- package/Delete.js +0 -1
- package/Drop.js +0 -1
- package/Insert.js +0 -1
- package/Makefile +0 -8
- package/README.md +0 -0
- package/Select.js +0 -1
- package/Table.js +0 -1
- package/Transaction.js +0 -1
- package/Types.js +0 -1
- package/Update.js +0 -1
- package/alchemy.js +0 -57
- package/alchemy.sublime-project +0 -9
- package/alchemy.sublime-workspace +0 -2304
- package/config/app.js +0 -8
- package/config/db.js +0 -20
- package/copy.js +0 -1
- package/defaultEngine.js +0 -8
- package/engine.js +0 -282
- package/getInstantiatorFunction.js +0 -28
- package/index.js +0 -52
- package/jstest.js +0 -3
- package/loggerFactory.js +0 -27
- package/logs/alchemy.log +0 -46
- package/logs/sql.log +0 -41
- package/sql/Column.js +0 -158
- package/sql/Columnable.js +0 -21
- package/sql/Create.js +0 -15
- package/sql/Delete.js +0 -34
- package/sql/Drop.js +0 -36
- package/sql/Insert.js +0 -96
- package/sql/SchemaElement.js +0 -11
- package/sql/Select.js +0 -192
- package/sql/SqlBase.js +0 -134
- package/sql/SqlStatement.js +0 -80
- package/sql/Table.js +0 -96
- package/sql/Transaction.js +0 -45
- package/sql/Update.js +0 -105
- package/sql/defineElement.js +0 -26
- package/sql/defineStatement.js +0 -26
- package/sql/types/BaseType.js +0 -43
- package/sql/types/BigSerialType.js +0 -5
- package/sql/types/IntegerType.js +0 -5
- package/sql/types/JsonType.js +0 -5
- package/sql/types/SerialType.js +0 -5
- package/sql/types/StringType.js +0 -31
- package/sql/types/TimestampType.js +0 -5
- package/sql/types/UUIDType.js +0 -5
- package/sql/types/defineType.js +0 -25
- package/sql/types/getType.js +0 -12
- package/sql/types/index.js +0 -11
- package/sql/where.js +0 -31
- package/sql/whereToString.js +0 -29
- package/test/ColumnTests.js +0 -80
- package/test/CreateTable.js +0 -54
- package/test/DeleteTests.js +0 -28
- package/test/DropTests.js +0 -23
- package/test/GetTypeTests.js +0 -56
- package/test/InsertTests.js +0 -86
- package/test/SelectExistsTests.js +0 -26
- package/test/SelectTests.js +0 -130
- package/test/TableTests.js +0 -46
- package/test.js +0 -8
- package/test.ls +0 -4
- package/validate.js +0 -26
- package/x.js +0 -5
package/package.json
CHANGED
|
@@ -1,28 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "alchemy",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"license": "MIT",
|
|
13
|
-
"dependencies": {
|
|
14
|
-
"pg": "~3.4.0",
|
|
15
|
-
"expect.js": "^0.3.0",
|
|
16
|
-
"functionally": "^0.2.1",
|
|
17
|
-
"classy": "^1.4.0",
|
|
18
|
-
"bluebird": "~2.2.2",
|
|
19
|
-
"bunyan": "^0.23.1",
|
|
20
|
-
"jsesc": "~0.4.3",
|
|
21
|
-
"newify": "~1.0.0",
|
|
22
|
-
"ustring": "^1.1.1"
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"module": "index.ts",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./src/index.ts",
|
|
8
|
+
"./agent": "./src/components/agent/index.ts",
|
|
9
|
+
"./fs": "./src/components/fs/index.ts",
|
|
10
|
+
"./aws": "./src/components/aws/index.ts",
|
|
11
|
+
"./esbuild": "./src/components/esbuild.ts"
|
|
23
12
|
},
|
|
24
13
|
"devDependencies": {
|
|
25
|
-
"
|
|
26
|
-
|
|
14
|
+
"@types/bun": "latest"
|
|
15
|
+
},
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"@ai-sdk/openai": "^1.0.0",
|
|
18
|
+
"@aws-sdk/client-dynamodb": "^3.0.0",
|
|
19
|
+
"@aws-sdk/client-iam": "^3.0.0",
|
|
20
|
+
"@aws-sdk/client-lambda": "^3.0.0",
|
|
21
|
+
"@aws-sdk/client-s3": "^3.0.0",
|
|
22
|
+
"@aws-sdk/client-sqs": "^3.0.1",
|
|
23
|
+
"ai": "^4.0.0",
|
|
24
|
+
"jszip": "^3.0.0",
|
|
25
|
+
"esbuild": "^0.24.2",
|
|
26
|
+
"turndown": "^7.2.0",
|
|
27
|
+
"zod": "^3.24.1"
|
|
27
28
|
}
|
|
28
29
|
}
|
package/src/alchemize.ts
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { evaluate } from "./apply";
|
|
2
|
+
import { destroy } from "./destroy";
|
|
3
|
+
import { defaultStage, nodes, providers, stateStore } from "./global";
|
|
4
|
+
import type { StateStore } from "./state";
|
|
5
|
+
|
|
6
|
+
let finalized = false;
|
|
7
|
+
|
|
8
|
+
export interface AlchemizeOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Determines whether the resources will be created/updated or deleted.
|
|
11
|
+
*
|
|
12
|
+
* @default "up"
|
|
13
|
+
*/
|
|
14
|
+
mode?: "up" | "destroy";
|
|
15
|
+
/**
|
|
16
|
+
* Name to scope the resource state under (e.g. `.alchemy/{stage}/..`).
|
|
17
|
+
*
|
|
18
|
+
* @default - your POSIX username
|
|
19
|
+
*/
|
|
20
|
+
stage?: string;
|
|
21
|
+
/**
|
|
22
|
+
* If true, will not prune resources that were dropped from the root stack.
|
|
23
|
+
*
|
|
24
|
+
* @default true
|
|
25
|
+
*/
|
|
26
|
+
destroyOrphans?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* A custom state store to use instead of the default file system store.
|
|
29
|
+
*/
|
|
30
|
+
stateStore?: StateStore;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Explicitly finalize the program by deleting any resources that were dropped from the root stack.
|
|
35
|
+
*
|
|
36
|
+
* By default, this will be called when
|
|
37
|
+
*/
|
|
38
|
+
export async function alchemize(options?: AlchemizeOptions) {
|
|
39
|
+
if (finalized) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
finalized = true;
|
|
43
|
+
|
|
44
|
+
const stage = options?.stage ?? defaultStage;
|
|
45
|
+
const state = options?.stateStore ?? stateStore;
|
|
46
|
+
|
|
47
|
+
await state.init?.();
|
|
48
|
+
|
|
49
|
+
// Track in-progress deletions to avoid duplicate work
|
|
50
|
+
const deletionPromises = new Map<string, Promise<void>>();
|
|
51
|
+
|
|
52
|
+
const priorStates = await state.all(stage);
|
|
53
|
+
const priorIDs = Object.keys(priorStates);
|
|
54
|
+
|
|
55
|
+
const mode = options?.mode ?? "up";
|
|
56
|
+
|
|
57
|
+
if (mode === "up") {
|
|
58
|
+
await Promise.allSettled(
|
|
59
|
+
Array.from(nodes.values())
|
|
60
|
+
.reverse()
|
|
61
|
+
.map((node) =>
|
|
62
|
+
evaluate(node.resource, {
|
|
63
|
+
stage,
|
|
64
|
+
}),
|
|
65
|
+
),
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
if (options?.destroyOrphans === false) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const aliveIDs = new Set(
|
|
73
|
+
mode === "up"
|
|
74
|
+
? nodes.keys()
|
|
75
|
+
: // There are no alive resources in destroy mode
|
|
76
|
+
[],
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const orphanIDs = Array.from(priorIDs).filter((id) => !aliveIDs.has(id));
|
|
80
|
+
|
|
81
|
+
const orphanStates = Object.fromEntries(
|
|
82
|
+
orphanIDs.map((id) => [id, priorStates[id]] as const),
|
|
83
|
+
);
|
|
84
|
+
// compute a map of resourceID -> upstream dependencies (resources that depend on it)
|
|
85
|
+
const orphanGraph: Record<string, Set<string>> = {};
|
|
86
|
+
for (const [orphanID, orphanState] of Object.entries(orphanStates)) {
|
|
87
|
+
orphanGraph[orphanID] ??= new Set();
|
|
88
|
+
for (const dep of orphanState.deps) {
|
|
89
|
+
(orphanGraph[dep] ??= new Set()).add(orphanID);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Start deletion from each orphan that has no dependents (nothing depends on it)
|
|
94
|
+
await Promise.all(
|
|
95
|
+
orphanIDs
|
|
96
|
+
.filter((id) => orphanGraph[id].size === 0)
|
|
97
|
+
.map((id) => deleteOrphan(id)),
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
// Recursively delete orphans in dependency order
|
|
101
|
+
async function deleteOrphan(orphanID: string): Promise<void> {
|
|
102
|
+
// Return existing deletion promise if this orphan is already being deleted
|
|
103
|
+
const existing = deletionPromises.get(orphanID);
|
|
104
|
+
if (existing) {
|
|
105
|
+
return existing;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Create promise immediately to prevent race conditions
|
|
109
|
+
let resolve: () => void;
|
|
110
|
+
let reject: (error: any) => void;
|
|
111
|
+
const promise = new Promise<void>((res, rej) => {
|
|
112
|
+
resolve = res;
|
|
113
|
+
reject = rej;
|
|
114
|
+
});
|
|
115
|
+
deletionPromises.set(orphanID, promise);
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
const orphanState = orphanStates[orphanID];
|
|
119
|
+
|
|
120
|
+
// First delete this resource
|
|
121
|
+
const providerType = orphanState.provider;
|
|
122
|
+
const provider = providers.get(providerType);
|
|
123
|
+
if (!provider) {
|
|
124
|
+
throw new Error(
|
|
125
|
+
`No provider found for ${providerType}. Did you forget to import it?`,
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
await destroy(stage, orphanID, orphanState, provider);
|
|
129
|
+
|
|
130
|
+
// After this resource is deleted, we can delete its dependencies if they're orphans
|
|
131
|
+
if (orphanState.deps.length > 0) {
|
|
132
|
+
await Promise.all(
|
|
133
|
+
orphanState.deps
|
|
134
|
+
.filter((dep) => orphanIDs.includes(dep)) // Only delete if it's an orphan
|
|
135
|
+
.map((dep) => deleteOrphan(dep)),
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
resolve!();
|
|
140
|
+
} catch (error) {
|
|
141
|
+
reject!(error);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return promise;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (process.env.ALCHEMY_NO_DEPLOY !== "true") {
|
|
149
|
+
// Listen for signals or events
|
|
150
|
+
process.on("exit", () => alchemize());
|
|
151
|
+
}
|
package/src/apply.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { defaultStage, stateStore } from "./global";
|
|
2
|
+
import { Output } from "./output";
|
|
3
|
+
import {
|
|
4
|
+
Input,
|
|
5
|
+
Provider,
|
|
6
|
+
type Resource,
|
|
7
|
+
ResourceID,
|
|
8
|
+
isResource,
|
|
9
|
+
} from "./resource";
|
|
10
|
+
import type { StateStore } from "./state";
|
|
11
|
+
|
|
12
|
+
interface ApplyOptions {
|
|
13
|
+
stage?: string;
|
|
14
|
+
stateStore?: StateStore;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Apply a sub-graph to produce a resource.
|
|
19
|
+
* @param output A sub-graph that produces a resource.
|
|
20
|
+
* @returns The resource properties.
|
|
21
|
+
*/
|
|
22
|
+
export async function apply<T>(
|
|
23
|
+
output: T | Output<T>,
|
|
24
|
+
options?: ApplyOptions,
|
|
25
|
+
): Promise<T> {
|
|
26
|
+
return (await evaluate(output, options)).value;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
class Evaluated<T> {
|
|
30
|
+
constructor(
|
|
31
|
+
public readonly value: T,
|
|
32
|
+
public readonly deps: string[] = [],
|
|
33
|
+
) {}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const cache = new WeakMap<Resource, Promise<Evaluated<any>>>();
|
|
37
|
+
|
|
38
|
+
export async function evaluate<T>(
|
|
39
|
+
output: T | Output<T>,
|
|
40
|
+
options: ApplyOptions = {},
|
|
41
|
+
): Promise<Evaluated<T>> {
|
|
42
|
+
const state = options.stateStore ?? stateStore;
|
|
43
|
+
if (isResource(output)) {
|
|
44
|
+
const resource = output;
|
|
45
|
+
const resourceID = resource[ResourceID];
|
|
46
|
+
const evaluated = await Promise.all(
|
|
47
|
+
resource[Input].map((r) => evaluate<any>(r, options)),
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
if (cache.has(resource)) {
|
|
51
|
+
return await cache.get(resource)!;
|
|
52
|
+
}
|
|
53
|
+
let resolve: (value: Evaluated<any>) => void;
|
|
54
|
+
let reject: (reason?: any) => void;
|
|
55
|
+
const promise = new Promise<Evaluated<any>>((res, rej) => {
|
|
56
|
+
resolve = res;
|
|
57
|
+
reject = rej;
|
|
58
|
+
});
|
|
59
|
+
// set this eagerly to avoid double-apply (caused by recursive applies to inputs)
|
|
60
|
+
cache.set(resource, promise);
|
|
61
|
+
|
|
62
|
+
const deps = new Set(evaluated.flatMap((input) => input.deps));
|
|
63
|
+
const inputs = evaluated.map((input) => input.value);
|
|
64
|
+
try {
|
|
65
|
+
const result: T = await resource[Provider].update(
|
|
66
|
+
options.stage ?? defaultStage,
|
|
67
|
+
resource,
|
|
68
|
+
deps,
|
|
69
|
+
inputs as [],
|
|
70
|
+
state,
|
|
71
|
+
);
|
|
72
|
+
resolve!(new Evaluated(result, [resourceID, ...deps]));
|
|
73
|
+
} catch (error) {
|
|
74
|
+
reject!(error);
|
|
75
|
+
}
|
|
76
|
+
return promise;
|
|
77
|
+
} else if (output instanceof Output) {
|
|
78
|
+
const inside = output as unknown as {
|
|
79
|
+
parent: Output<any>;
|
|
80
|
+
fn: (value: any) => T;
|
|
81
|
+
};
|
|
82
|
+
const parent = await evaluate(inside.parent, options);
|
|
83
|
+
const ret = inside.fn(parent.value);
|
|
84
|
+
// the ret may be an Output (e.g. in the flatMap case), so we need to evaluate it and include its deps
|
|
85
|
+
const evaluated = await evaluate(ret, options);
|
|
86
|
+
return new Evaluated<T>(evaluated.value, [
|
|
87
|
+
...parent.deps,
|
|
88
|
+
...evaluated.deps,
|
|
89
|
+
]);
|
|
90
|
+
} else if (Array.isArray(output)) {
|
|
91
|
+
const evaluatedItems = await Promise.all(
|
|
92
|
+
output.map((item) => evaluate(item, options)),
|
|
93
|
+
);
|
|
94
|
+
return new Evaluated(
|
|
95
|
+
evaluatedItems.map((e) => e.value) as unknown as T,
|
|
96
|
+
evaluatedItems.flatMap((e) => e.deps),
|
|
97
|
+
);
|
|
98
|
+
} else if (output && typeof output === "object") {
|
|
99
|
+
const entries = Object.entries(output);
|
|
100
|
+
const evaluatedEntries = await Promise.all(
|
|
101
|
+
entries.map(
|
|
102
|
+
async ([key, value]) => [key, await evaluate(value, options)] as const,
|
|
103
|
+
),
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const result = Object.fromEntries(
|
|
107
|
+
evaluatedEntries.map(([key, evaluated]) => [key, evaluated.value]),
|
|
108
|
+
) as unknown as T;
|
|
109
|
+
|
|
110
|
+
const deps = evaluatedEntries.flatMap(([_, evaluated]) => evaluated.deps);
|
|
111
|
+
|
|
112
|
+
return new Evaluated(result, deps);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Base case: primitive value
|
|
116
|
+
return new Evaluated<T>(output as T);
|
|
117
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ZodType } from "zod";
|
|
2
|
+
import { type Context, type Provider, Resource } from "../../resource";
|
|
3
|
+
|
|
4
|
+
export function Agent<Type extends string, TInput, TOutput>(
|
|
5
|
+
type: Type,
|
|
6
|
+
{
|
|
7
|
+
description,
|
|
8
|
+
input,
|
|
9
|
+
output,
|
|
10
|
+
}: {
|
|
11
|
+
description: string;
|
|
12
|
+
input: ZodType<TInput>;
|
|
13
|
+
output: ZodType<TOutput>;
|
|
14
|
+
},
|
|
15
|
+
handler: (ctx: Context<TOutput>, props: TInput) => Promise<TOutput | void>,
|
|
16
|
+
): Provider<Type, [TInput], Awaited<TOutput>> & {
|
|
17
|
+
description: string;
|
|
18
|
+
input: ZodType<TInput>;
|
|
19
|
+
output: ZodType<TOutput>;
|
|
20
|
+
} {
|
|
21
|
+
// @ts-expect-error - we don't care that the props are not statically known, they will be visible outside this scope
|
|
22
|
+
return class Agent extends Resource(
|
|
23
|
+
type,
|
|
24
|
+
async (ctx: Context<TOutput>, props: TInput): Promise<TOutput | void> => {
|
|
25
|
+
// Validate input
|
|
26
|
+
const validatedInput = input.parse(props);
|
|
27
|
+
|
|
28
|
+
// Call handler with validated input
|
|
29
|
+
const result = await handler(ctx, validatedInput);
|
|
30
|
+
|
|
31
|
+
if (ctx.event === "delete") {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Validate output
|
|
36
|
+
const validatedOutput = output.parse(result);
|
|
37
|
+
|
|
38
|
+
return validatedOutput;
|
|
39
|
+
},
|
|
40
|
+
) {
|
|
41
|
+
static readonly description = description;
|
|
42
|
+
static readonly input = input;
|
|
43
|
+
static readonly output = output;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @see https://docs.anthropic.com/en/docs/about-claude/models#model-comparison-table
|
|
3
|
+
*/
|
|
4
|
+
export const AnthropicModels = [
|
|
5
|
+
// Claude 3.5 Sonnet models
|
|
6
|
+
"claude-3-5-sonnet",
|
|
7
|
+
"claude-3-5-sonnet-20241022",
|
|
8
|
+
"claude-3-5-sonnet-20240620",
|
|
9
|
+
|
|
10
|
+
// Claude 3.5 Haiku models
|
|
11
|
+
"claude-3-5-haiku",
|
|
12
|
+
"claude-3-5-haiku-20241022",
|
|
13
|
+
|
|
14
|
+
// Claude 3 models
|
|
15
|
+
"claude-3-opus",
|
|
16
|
+
"claude-3-opus-20240229",
|
|
17
|
+
"claude-3-sonnet",
|
|
18
|
+
"claude-3-sonnet-20240229",
|
|
19
|
+
"claude-3-haiku",
|
|
20
|
+
"claude-3-haiku-20240307",
|
|
21
|
+
] as const;
|
|
22
|
+
|
|
23
|
+
export type AnthropicModelId = (typeof AnthropicModels)[number] | (string & {});
|
|
24
|
+
|
|
25
|
+
export function isAnthropicModel(model: string): model is AnthropicModelId {
|
|
26
|
+
return model.startsWith("claude-");
|
|
27
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type AnthropicModelId, isAnthropicModel } from "./anthropic";
|
|
2
|
+
import { type OpenAIChatModelId, isOpenAIModel } from "./openai";
|
|
3
|
+
|
|
4
|
+
export type ModelId = OpenAIChatModelId | AnthropicModelId;
|
|
5
|
+
|
|
6
|
+
export async function resolveModel(modelId: ModelId) {
|
|
7
|
+
if (isOpenAIModel(modelId)) {
|
|
8
|
+
const { openai } = await import("@ai-sdk/openai");
|
|
9
|
+
return {
|
|
10
|
+
type: "openai" as const,
|
|
11
|
+
model: openai(modelId as OpenAIChatModelId),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
if (isAnthropicModel(modelId)) {
|
|
15
|
+
const { anthropic } = await import("@ai-sdk/anthropic");
|
|
16
|
+
return {
|
|
17
|
+
type: "anthropic" as const,
|
|
18
|
+
model: anthropic(modelId as AnthropicModelId),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
throw new Error(`Unsupported model ID: ${modelId}`);
|
|
22
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export const OpenAIModels = [
|
|
2
|
+
// O1 models
|
|
3
|
+
"o1",
|
|
4
|
+
"o1-2024-12-17",
|
|
5
|
+
"o1-mini",
|
|
6
|
+
"o1-mini-2024-09-12",
|
|
7
|
+
"o1-preview",
|
|
8
|
+
"o1-preview-2024-09-12",
|
|
9
|
+
|
|
10
|
+
// GPT-4O models
|
|
11
|
+
"gpt-4o",
|
|
12
|
+
"gpt-4o-2024-05-13",
|
|
13
|
+
"gpt-4o-2024-08-06",
|
|
14
|
+
"gpt-4o-2024-11-20",
|
|
15
|
+
"gpt-4o-audio-preview",
|
|
16
|
+
"gpt-4o-audio-preview-2024-10-01",
|
|
17
|
+
"gpt-4o-audio-preview-2024-12-17",
|
|
18
|
+
"gpt-4o-mini",
|
|
19
|
+
"gpt-4o-mini-2024-07-18",
|
|
20
|
+
|
|
21
|
+
// GPT-4 models
|
|
22
|
+
"gpt-4-turbo",
|
|
23
|
+
"gpt-4-turbo-2024-04-09",
|
|
24
|
+
"gpt-4-turbo-preview",
|
|
25
|
+
"gpt-4-0125-preview",
|
|
26
|
+
"gpt-4-1106-preview",
|
|
27
|
+
"gpt-4",
|
|
28
|
+
"gpt-4-0613",
|
|
29
|
+
|
|
30
|
+
// GPT-3.5 models
|
|
31
|
+
"gpt-3.5-turbo-0125",
|
|
32
|
+
"gpt-3.5-turbo",
|
|
33
|
+
"gpt-3.5-turbo-1106",
|
|
34
|
+
] as const;
|
|
35
|
+
|
|
36
|
+
export type OpenAIChatModelId = (typeof OpenAIModels)[number] | (string & {});
|
|
37
|
+
|
|
38
|
+
export function isOpenAIModel(model: string): model is OpenAIChatModelId {
|
|
39
|
+
return (
|
|
40
|
+
model.startsWith("gpt-") ||
|
|
41
|
+
model.startsWith("o1") ||
|
|
42
|
+
model.startsWith("gpt-4o")
|
|
43
|
+
);
|
|
44
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { generateObject } from "ai";
|
|
2
|
+
import { mkdir, unlink, writeFile } from "fs/promises";
|
|
3
|
+
import { dirname } from "path";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { Agent } from "./agent";
|
|
6
|
+
import type { AnthropicModelId } from "./anthropic";
|
|
7
|
+
import { resolveModel } from "./model";
|
|
8
|
+
import type { OpenAIChatModelId } from "./openai";
|
|
9
|
+
|
|
10
|
+
export type ModelId = OpenAIChatModelId | AnthropicModelId;
|
|
11
|
+
|
|
12
|
+
export const PackageJsonSchema = z.object({
|
|
13
|
+
name: z.string(),
|
|
14
|
+
version: z.string(),
|
|
15
|
+
description: z.string().optional(),
|
|
16
|
+
main: z.string().optional(),
|
|
17
|
+
types: z.string().optional(),
|
|
18
|
+
type: z.literal("module"),
|
|
19
|
+
scripts: z.record(z.string()).optional(),
|
|
20
|
+
peerDependencies: z.record(z.string()).optional(),
|
|
21
|
+
peerDependenciesMeta: z
|
|
22
|
+
.record(z.object({ optional: z.boolean() }))
|
|
23
|
+
.optional(),
|
|
24
|
+
devDependencies: z.record(z.string()).optional(),
|
|
25
|
+
keywords: z.array(z.string()).optional(),
|
|
26
|
+
author: z.string().optional(),
|
|
27
|
+
license: z.string().optional(),
|
|
28
|
+
repository: z
|
|
29
|
+
.object({
|
|
30
|
+
type: z.string(),
|
|
31
|
+
url: z.string(),
|
|
32
|
+
})
|
|
33
|
+
.optional(),
|
|
34
|
+
exports: z.unknown().optional(),
|
|
35
|
+
files: z.array(z.string()).optional(),
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export const PackageInput = z.object({
|
|
39
|
+
/**
|
|
40
|
+
* List of requirements for the package
|
|
41
|
+
* Can include dependencies, dev dependencies, scripts, etc.
|
|
42
|
+
*/
|
|
43
|
+
requirements: z.array(z.string()),
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Name of the package
|
|
47
|
+
*/
|
|
48
|
+
name: z.string(),
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The ID of the model to use for generating package configuration
|
|
52
|
+
* @default "gpt-4o"
|
|
53
|
+
*/
|
|
54
|
+
model: z.string().optional(),
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Temperature setting for model generation
|
|
58
|
+
* @default 0.3
|
|
59
|
+
*/
|
|
60
|
+
temperature: z.number().optional(),
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Path to the package.json file to generate
|
|
64
|
+
*/
|
|
65
|
+
path: z.string(),
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
export type PackageInput = z.infer<typeof PackageInput>;
|
|
69
|
+
export type PackageOutput = z.infer<typeof PackageJsonSchema>;
|
|
70
|
+
|
|
71
|
+
export class PackageJson extends Agent(
|
|
72
|
+
"code::Package",
|
|
73
|
+
{
|
|
74
|
+
description:
|
|
75
|
+
"This Agent is responsible for generating package.json configuration based on requirements.",
|
|
76
|
+
input: PackageInput,
|
|
77
|
+
output: PackageJsonSchema,
|
|
78
|
+
},
|
|
79
|
+
async (ctx, input) => {
|
|
80
|
+
if (ctx.event === "delete") {
|
|
81
|
+
await unlink(input.path);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Get the appropriate model
|
|
86
|
+
const { model } = await resolveModel(input.model ?? "gpt-4o");
|
|
87
|
+
|
|
88
|
+
// Generate the package configuration using generateObject for type safety
|
|
89
|
+
const result = await generateObject({
|
|
90
|
+
model,
|
|
91
|
+
schema: PackageJsonSchema,
|
|
92
|
+
temperature: input.temperature ?? 0.3,
|
|
93
|
+
messages: [
|
|
94
|
+
{
|
|
95
|
+
role: "system",
|
|
96
|
+
content:
|
|
97
|
+
"You are an expert at creating Node.js/TypeScript package configurations. You will generate a package.json configuration based on requirements.",
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
role: "user",
|
|
101
|
+
content: `Please generate a package.json configuration based on these requirements:
|
|
102
|
+
|
|
103
|
+
Package name: ${input.name}
|
|
104
|
+
|
|
105
|
+
Requirements:
|
|
106
|
+
${input.requirements.map((req) => `- ${req}`).join("\n")}
|
|
107
|
+
|
|
108
|
+
Rules:
|
|
109
|
+
1. All dependencies must be peer dependencies
|
|
110
|
+
2. Use latest stable versions for all dependencies
|
|
111
|
+
3. Include standard TypeScript configuration in devDependencies
|
|
112
|
+
4. Use "bun" as the package manager
|
|
113
|
+
5. Must use ESM modules (type: "module")`,
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Ensure the directory exists
|
|
119
|
+
await mkdir(dirname(input.path), { recursive: true });
|
|
120
|
+
|
|
121
|
+
// Write the package.json file
|
|
122
|
+
await writeFile(input.path, JSON.stringify(result.object, null, 2));
|
|
123
|
+
|
|
124
|
+
return result.object;
|
|
125
|
+
},
|
|
126
|
+
) {}
|