alchemy 0.2.5 → 0.3.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.
Files changed (197) hide show
  1. package/README.md +3 -3
  2. package/lib/ai/approve.d.ts +99 -0
  3. package/lib/ai/approve.js +76 -0
  4. package/lib/ai/astro-file.d.ts +137 -0
  5. package/lib/ai/astro-file.js +156 -0
  6. package/lib/ai/client.d.ts +7 -8
  7. package/lib/ai/client.js +48 -19
  8. package/lib/ai/css-file.d.ts +131 -0
  9. package/lib/ai/css-file.js +141 -0
  10. package/lib/ai/data.d.ts +51 -6
  11. package/lib/ai/data.js +58 -12
  12. package/lib/ai/document.d.ts +108 -24
  13. package/lib/ai/document.js +166 -52
  14. package/lib/ai/html-file.d.ts +128 -0
  15. package/lib/ai/html-file.js +138 -0
  16. package/lib/ai/index.d.ts +9 -0
  17. package/lib/ai/index.js +9 -0
  18. package/lib/ai/json-file.d.ts +160 -0
  19. package/lib/ai/json-file.js +187 -0
  20. package/lib/ai/review.d.ts +122 -0
  21. package/lib/ai/review.js +101 -0
  22. package/lib/ai/typescript-file.d.ts +139 -0
  23. package/lib/ai/typescript-file.js +145 -0
  24. package/lib/ai/vue-file.d.ts +126 -0
  25. package/lib/ai/vue-file.js +136 -0
  26. package/lib/ai/yaml-file.d.ts +169 -0
  27. package/lib/ai/yaml-file.js +200 -0
  28. package/lib/alchemy.js +17 -2
  29. package/lib/apply.js +1 -1
  30. package/lib/cloudflare/dns.d.ts +88 -0
  31. package/lib/cloudflare/dns.js +192 -0
  32. package/lib/cloudflare/generate-asset-manifest.js +1 -1
  33. package/lib/cloudflare/index.d.ts +2 -1
  34. package/lib/cloudflare/index.js +2 -1
  35. package/lib/cloudflare/r2-rest-state-store.d.ts +125 -0
  36. package/lib/cloudflare/r2-rest-state-store.js +254 -0
  37. package/lib/cloudflare/response.d.ts +12 -0
  38. package/lib/cloudflare/response.js +0 -0
  39. package/lib/cloudflare/static-site-router.js +16 -7
  40. package/lib/cloudflare/static-site.js +3 -0
  41. package/lib/cloudflare/upload-asset-manifest.js +4 -2
  42. package/lib/cloudflare/zone.d.ts +18 -0
  43. package/lib/cloudflare/zone.js +17 -4
  44. package/lib/dns/godaddy.d.ts +7 -0
  45. package/lib/dns/godaddy.js +21 -0
  46. package/lib/dns/import-dns.d.ts +82 -0
  47. package/lib/dns/import-dns.js +114 -0
  48. package/lib/dns/index.d.ts +2 -0
  49. package/lib/dns/index.js +2 -0
  50. package/lib/dns/record.d.ts +88 -0
  51. package/lib/dns/record.js +13 -0
  52. package/lib/fs/copy-file.d.ts +54 -0
  53. package/lib/fs/copy-file.js +63 -0
  54. package/lib/fs/file-system-state-store.d.ts +17 -0
  55. package/lib/fs/file-system-state-store.js +87 -0
  56. package/lib/fs/file.d.ts +13 -0
  57. package/lib/fs/file.js +24 -5
  58. package/lib/fs/folder.d.ts +10 -0
  59. package/lib/fs/folder.js +2 -2
  60. package/lib/fs/index.d.ts +10 -4
  61. package/lib/fs/index.js +10 -4
  62. package/lib/fs/static-astro-file.d.ts +34 -0
  63. package/lib/fs/static-astro-file.js +39 -0
  64. package/lib/fs/static-css-file.d.ts +24 -0
  65. package/lib/fs/static-css-file.js +29 -0
  66. package/lib/fs/static-html-file.d.ts +31 -0
  67. package/lib/fs/static-html-file.js +36 -0
  68. package/lib/fs/{json-file.d.ts → static-json-file.d.ts} +3 -3
  69. package/lib/fs/static-json-file.js +15 -0
  70. package/lib/fs/{text-file.d.ts → static-text-file.d.ts} +2 -2
  71. package/lib/fs/static-text-file.js +8 -0
  72. package/lib/fs/{typescript-file.d.ts → static-typescript-file.d.ts} +3 -3
  73. package/lib/fs/{typescript-file.js → static-typescript-file.js} +3 -2
  74. package/lib/fs/static-vue-file.d.ts +28 -0
  75. package/lib/fs/static-vue-file.js +33 -0
  76. package/lib/fs/static-yaml-file.d.ts +22 -0
  77. package/lib/fs/{yaml-file.js → static-yaml-file.js} +3 -2
  78. package/lib/internal/getting-started.d.ts +21 -0
  79. package/lib/internal/getting-started.js +87 -0
  80. package/lib/internal/index.d.ts +3 -0
  81. package/lib/internal/index.js +3 -0
  82. package/lib/internal/providers.d.ts +27 -0
  83. package/lib/internal/providers.js +172 -0
  84. package/lib/internal/tutorial.d.ts +104 -0
  85. package/lib/internal/tutorial.js +251 -0
  86. package/lib/resource.js +8 -1
  87. package/lib/scope.d.ts +1 -1
  88. package/lib/scope.js +4 -2
  89. package/lib/state.d.ts +1 -16
  90. package/lib/state.js +0 -87
  91. package/lib/test/bun.d.ts +11 -0
  92. package/lib/test/bun.js +36 -8
  93. package/lib/web/astro.d.ts +147 -0
  94. package/lib/web/astro.js +414 -0
  95. package/lib/{shadcn/component.js → web/shadcn-component.js} +2 -9
  96. package/lib/web/shadcn.d.ts +97 -0
  97. package/lib/web/shadcn.js +99 -0
  98. package/lib/web/tailwind.d.ts +64 -0
  99. package/lib/web/tailwind.js +97 -0
  100. package/lib/{vite → web}/vite.d.ts +5 -0
  101. package/lib/{vite → web}/vite.js +44 -44
  102. package/lib/web/vitepress/config.d.ts +10 -0
  103. package/lib/web/vitepress/config.js +31 -0
  104. package/lib/web/vitepress/custom-theme.d.ts +173 -0
  105. package/lib/web/vitepress/custom-theme.js +336 -0
  106. package/lib/{vitepress → web/vitepress}/dependencies.d.ts +2 -2
  107. package/lib/{vitepress → web/vitepress}/dependencies.js +1 -1
  108. package/lib/web/vitepress/home-page.d.ts +246 -0
  109. package/lib/web/vitepress/home-page.js +147 -0
  110. package/lib/{vitepress → web/vitepress}/index.d.ts +2 -0
  111. package/lib/{vitepress → web/vitepress}/index.js +2 -0
  112. package/lib/{vitepress → web/vitepress}/vitepress.d.ts +15 -12
  113. package/lib/{vitepress → web/vitepress}/vitepress.js +26 -44
  114. package/package.json +6 -2
  115. package/src/ai/approve.ts +163 -0
  116. package/src/ai/astro-file.ts +269 -0
  117. package/src/ai/client.ts +60 -21
  118. package/src/ai/css-file.ts +247 -0
  119. package/src/ai/data.ts +81 -19
  120. package/src/ai/document.ts +240 -62
  121. package/src/ai/html-file.ts +244 -0
  122. package/src/ai/index.ts +9 -0
  123. package/src/ai/json-file.ts +325 -0
  124. package/src/ai/review.ts +213 -0
  125. package/src/ai/typescript-file.ts +259 -0
  126. package/src/ai/vue-file.ts +242 -0
  127. package/src/ai/yaml-file.ts +328 -0
  128. package/src/alchemy.ts +18 -7
  129. package/src/apply.ts +8 -9
  130. package/src/cloudflare/dns.ts +344 -0
  131. package/src/cloudflare/generate-asset-manifest.ts +4 -4
  132. package/src/cloudflare/index.ts +2 -1
  133. package/src/cloudflare/r2-rest-state-store.ts +353 -0
  134. package/src/cloudflare/response.ts +9 -0
  135. package/src/cloudflare/static-site-router.ts +18 -8
  136. package/src/cloudflare/static-site.ts +11 -7
  137. package/src/cloudflare/upload-asset-manifest.ts +6 -4
  138. package/src/cloudflare/worker.ts +44 -43
  139. package/src/cloudflare/zone.ts +39 -23
  140. package/src/dns/godaddy.ts +29 -0
  141. package/src/dns/import-dns.ts +213 -0
  142. package/src/dns/index.ts +2 -0
  143. package/src/dns/record.ts +121 -0
  144. package/src/fs/copy-file.ts +115 -0
  145. package/src/fs/file-system-state-store.ts +109 -0
  146. package/src/fs/file.ts +35 -9
  147. package/src/fs/folder.ts +14 -2
  148. package/src/fs/index.ts +10 -4
  149. package/src/fs/static-astro-file.ts +45 -0
  150. package/src/fs/static-css-file.ts +35 -0
  151. package/src/fs/static-html-file.ts +42 -0
  152. package/src/fs/static-json-file.ts +34 -0
  153. package/src/fs/{text-file.ts → static-text-file.ts} +9 -3
  154. package/src/fs/{typescript-file.ts → static-typescript-file.ts} +7 -6
  155. package/src/fs/static-vue-file.ts +39 -0
  156. package/src/fs/static-yaml-file.ts +33 -0
  157. package/src/internal/getting-started.ts +115 -0
  158. package/src/internal/index.ts +3 -0
  159. package/src/internal/providers.ts +241 -0
  160. package/src/internal/tutorial.ts +392 -0
  161. package/src/resource.ts +10 -1
  162. package/src/scope.ts +5 -6
  163. package/src/state.ts +1 -111
  164. package/src/test/bun.ts +60 -10
  165. package/src/web/astro.ts +644 -0
  166. package/src/{shadcn/component.ts → web/shadcn-component.ts} +5 -13
  167. package/src/web/shadcn.ts +219 -0
  168. package/src/web/tailwind.ts +167 -0
  169. package/src/{vite → web}/vite.ts +54 -56
  170. package/src/web/vitepress/config.ts +48 -0
  171. package/src/web/vitepress/custom-theme.ts +514 -0
  172. package/src/{vitepress → web/vitepress}/dependencies.ts +2 -2
  173. package/src/web/vitepress/home-page.ts +375 -0
  174. package/src/{vitepress → web/vitepress}/index.ts +2 -0
  175. package/src/{vitepress → web/vitepress}/vitepress.ts +62 -72
  176. package/lib/cloudflare/state.d.ts +0 -82
  177. package/lib/cloudflare/state.js +0 -108
  178. package/lib/fs/json-file.js +0 -7
  179. package/lib/fs/text-file.js +0 -7
  180. package/lib/fs/yaml-file.d.ts +0 -19
  181. package/lib/internal/docs.d.ts +0 -5
  182. package/lib/internal/docs.js +0 -287
  183. package/lib/shadcn/index.d.ts +0 -1
  184. package/lib/shadcn/index.js +0 -1
  185. package/lib/vite/index.d.ts +0 -1
  186. package/lib/vite/index.js +0 -1
  187. package/lib/vitepress/home-page.d.ts +0 -133
  188. package/lib/vitepress/home-page.js +0 -11
  189. package/src/cloudflare/state.ts +0 -138
  190. package/src/fs/json-file.ts +0 -23
  191. package/src/fs/yaml-file.ts +0 -26
  192. package/src/internal/docs.ts +0 -338
  193. package/src/shadcn/index.ts +0 -1
  194. package/src/vite/index.ts +0 -1
  195. package/src/vitepress/home-page.ts +0 -166
  196. /package/lib/{shadcn/component.d.ts → web/shadcn-component.d.ts} +0 -0
  197. /package/src/{vitepress → web/vitepress}/index.md +0 -0
@@ -0,0 +1,160 @@
1
+ import type { JsonSchema, Type, type } from "arktype";
2
+ import type { Context } from "../context";
3
+ import { Resource } from "../resource";
4
+ import type { Secret } from "../secret";
5
+ import { type ModelConfig } from "./client";
6
+ /**
7
+ * Properties for creating or updating a JSONFile
8
+ */
9
+ export interface JSONFileProps<T extends Type<any, any> | undefined = undefined> {
10
+ /**
11
+ * Path to the JSON file
12
+ */
13
+ path: string;
14
+ /**
15
+ * Optional ArkType schema to validate and structure the generated JSON
16
+ * When provided, the resource will use generateObject with schema validation
17
+ * When not provided, it will extract JSON from between ```json fences
18
+ */
19
+ schema?: T;
20
+ /**
21
+ * Base URL for the OpenAI API
22
+ * @default 'https://api.openai.com/v1'
23
+ */
24
+ baseURL?: string;
25
+ /**
26
+ * Prompt for generating content
27
+ * Use alchemy template literals to include file context:
28
+ * @example
29
+ * prompt: await alchemy`
30
+ * Generate a JSON configuration for:
31
+ * ${alchemy.file("src/config.ts")}
32
+ * `
33
+ */
34
+ prompt: string;
35
+ /**
36
+ * System prompt for the model
37
+ * This is used to provide instructions to the model about how to format the response
38
+ * @default Depends on whether schema is provided
39
+ */
40
+ system?: string;
41
+ /**
42
+ * OpenAI API key to use for generating content
43
+ * If not provided, will use OPENAI_API_KEY environment variable
44
+ */
45
+ apiKey?: Secret;
46
+ /**
47
+ * Model configuration
48
+ */
49
+ model?: ModelConfig;
50
+ /**
51
+ * Temperature for controlling randomness in generation.
52
+ * Higher values (e.g., 0.8) make output more random,
53
+ * lower values (e.g., 0.2) make it more deterministic.
54
+ * @default 0.7
55
+ */
56
+ temperature?: number;
57
+ /**
58
+ * Whether to pretty-print the JSON with indentation
59
+ * @default true
60
+ */
61
+ pretty?: boolean;
62
+ /**
63
+ * Number of spaces to use for indentation when pretty-printing
64
+ * @default 2
65
+ */
66
+ indent?: number;
67
+ }
68
+ /**
69
+ * A JSON file that can be created, updated, and deleted
70
+ */
71
+ export interface JSONFile<T = any> extends Omit<JSONFileProps, "schema">, Resource<"ai::JSONFile"> {
72
+ /**
73
+ * Content of the JSON file as a string
74
+ */
75
+ content: string;
76
+ /**
77
+ * Parsed JSON object
78
+ */
79
+ json: T;
80
+ /**
81
+ * Schema used to validate the JSON (if provided)
82
+ */
83
+ schema?: JsonSchema;
84
+ /**
85
+ * Time at which the file was created
86
+ */
87
+ createdAt: number;
88
+ /**
89
+ * Time at which the file was last updated
90
+ */
91
+ updatedAt: number;
92
+ }
93
+ /**
94
+ * Resource for generating JSON files using AI models.
95
+ * Can operate in two modes:
96
+ * 1. With schema: Uses generateObject with type validation
97
+ * 2. Without schema: Extracts JSON from between ```json fences
98
+ *
99
+ * @example
100
+ * // Generate a configuration file with freeform JSON
101
+ * const config = await JSONFile("app-config", {
102
+ * path: "./config/app.json",
103
+ * prompt: await alchemy`
104
+ * Generate a configuration for a web application with:
105
+ * - Server settings (port, host, timeout)
106
+ * - Database connection details (redact any passwords)
107
+ * - Logging configuration
108
+ * - Feature flags
109
+ * `,
110
+ * model: {
111
+ * id: "gpt-4o",
112
+ * provider: "openai"
113
+ * }
114
+ * });
115
+ *
116
+ * @example
117
+ * // Generate JSON with schema validation
118
+ * import { type } from "arktype";
119
+ *
120
+ * const userSchema = type({
121
+ * users: [{
122
+ * id: "string",
123
+ * name: "string",
124
+ * email: "string",
125
+ * role: "'admin' | 'user' | 'guest'",
126
+ * permissions: "string[]",
127
+ * active: "boolean"
128
+ * }]
129
+ * });
130
+ *
131
+ * const userData = await JSONFile("user-data", {
132
+ * path: "./data/users.json",
133
+ * schema: userSchema,
134
+ * prompt: "Generate sample user data for an application with various roles and permissions",
135
+ * temperature: 0.2
136
+ * });
137
+ *
138
+ * // Type-safe access to the generated data
139
+ * console.log(userData.json.users[0].role); // Typed as 'admin' | 'user' | 'guest'
140
+ *
141
+ * @example
142
+ * // Generate API mock data with custom system prompt
143
+ * const apiMock = await JSONFile("api-mock", {
144
+ * path: "./mocks/products-api.json",
145
+ * prompt: await alchemy`
146
+ * Create mock data for a product catalog API response with:
147
+ * - 10 products with different categories
148
+ * - Each product should have id, name, price, category, inventory, and image_url
149
+ * - Include pagination metadata (total, page, limit)
150
+ * `,
151
+ * system: "You are an API design expert. Create realistic mock JSON data that follows REST API best practices. Your response must be valid JSON inside ```json fences.",
152
+ * model: {
153
+ * id: "claude-3-opus-20240229",
154
+ * provider: "anthropic"
155
+ * },
156
+ * pretty: true,
157
+ * indent: 4
158
+ * });
159
+ */
160
+ export declare const JSONFile: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | (<const T extends Type<any, any> | undefined = undefined>(this: Context<JSONFile<T extends Type<any, any> ? type.infer<T> : any>>, id: string, props: JSONFileProps<T>) => Promise<JSONFile<T extends Type<any, any> ? type.infer<T> : any>>);
@@ -0,0 +1,187 @@
1
+ import { generateObject, generateText } from "ai";
2
+ import { StaticJsonFile } from "../fs/static-json-file";
3
+ import { Resource } from "../resource";
4
+ import { ark } from "./ark";
5
+ import { createModel } from "./client";
6
+ /**
7
+ * Default system prompt for JSON file generation without schema
8
+ */
9
+ const DEFAULT_JSON_SYSTEM_PROMPT = "You are a JSON generator. Create valid JSON based on the user's requirements. Your response MUST include only JSON inside ```json fences. Do not include any other text, explanations, or multiple code blocks.";
10
+ /**
11
+ * Resource for generating JSON files using AI models.
12
+ * Can operate in two modes:
13
+ * 1. With schema: Uses generateObject with type validation
14
+ * 2. Without schema: Extracts JSON from between ```json fences
15
+ *
16
+ * @example
17
+ * // Generate a configuration file with freeform JSON
18
+ * const config = await JSONFile("app-config", {
19
+ * path: "./config/app.json",
20
+ * prompt: await alchemy`
21
+ * Generate a configuration for a web application with:
22
+ * - Server settings (port, host, timeout)
23
+ * - Database connection details (redact any passwords)
24
+ * - Logging configuration
25
+ * - Feature flags
26
+ * `,
27
+ * model: {
28
+ * id: "gpt-4o",
29
+ * provider: "openai"
30
+ * }
31
+ * });
32
+ *
33
+ * @example
34
+ * // Generate JSON with schema validation
35
+ * import { type } from "arktype";
36
+ *
37
+ * const userSchema = type({
38
+ * users: [{
39
+ * id: "string",
40
+ * name: "string",
41
+ * email: "string",
42
+ * role: "'admin' | 'user' | 'guest'",
43
+ * permissions: "string[]",
44
+ * active: "boolean"
45
+ * }]
46
+ * });
47
+ *
48
+ * const userData = await JSONFile("user-data", {
49
+ * path: "./data/users.json",
50
+ * schema: userSchema,
51
+ * prompt: "Generate sample user data for an application with various roles and permissions",
52
+ * temperature: 0.2
53
+ * });
54
+ *
55
+ * // Type-safe access to the generated data
56
+ * console.log(userData.json.users[0].role); // Typed as 'admin' | 'user' | 'guest'
57
+ *
58
+ * @example
59
+ * // Generate API mock data with custom system prompt
60
+ * const apiMock = await JSONFile("api-mock", {
61
+ * path: "./mocks/products-api.json",
62
+ * prompt: await alchemy`
63
+ * Create mock data for a product catalog API response with:
64
+ * - 10 products with different categories
65
+ * - Each product should have id, name, price, category, inventory, and image_url
66
+ * - Include pagination metadata (total, page, limit)
67
+ * `,
68
+ * system: "You are an API design expert. Create realistic mock JSON data that follows REST API best practices. Your response must be valid JSON inside ```json fences.",
69
+ * model: {
70
+ * id: "claude-3-opus-20240229",
71
+ * provider: "anthropic"
72
+ * },
73
+ * pretty: true,
74
+ * indent: 4
75
+ * });
76
+ */
77
+ export const JSONFile = Resource("ai::JSONFile", async function (id, props) {
78
+ // Handle deletion phase
79
+ if (this.phase === "delete") {
80
+ return this.destroy();
81
+ }
82
+ let jsonContent;
83
+ let jsonObject;
84
+ // Check if schema is provided
85
+ if (props.schema) {
86
+ // Use schema-based generation
87
+ const { object } = await generateObject({
88
+ model: createModel(props),
89
+ schema: ark.schema(props.schema),
90
+ providerOptions: props.model?.options,
91
+ system: props.system ||
92
+ "Generate a valid JSON object based on the provided requirements.",
93
+ prompt: props.prompt,
94
+ ...(props.temperature === undefined
95
+ ? {}
96
+ : { temperature: props.temperature }),
97
+ });
98
+ jsonObject = object;
99
+ // Use StaticJsonFile to create the file
100
+ const file = await StaticJsonFile("file", props.path, jsonObject);
101
+ jsonContent = file.content;
102
+ }
103
+ else {
104
+ // Use fence-based extraction
105
+ // Use provided system prompt or default
106
+ const system = props.system || DEFAULT_JSON_SYSTEM_PROMPT;
107
+ // Generate initial content
108
+ const { text } = await generateText({
109
+ model: createModel(props),
110
+ prompt: props.prompt,
111
+ system,
112
+ providerOptions: props.model?.options,
113
+ ...(props.temperature === undefined
114
+ ? {}
115
+ : { temperature: props.temperature }),
116
+ });
117
+ // Extract and validate JSON content
118
+ let { content, error } = await extractJSONContent(text);
119
+ // Re-prompt if there are validation errors
120
+ if (error) {
121
+ const errorSystem = `${system}\n\nERROR: ${error}\n\nPlease try again and ensure your response contains exactly one valid JSON block inside \`\`\`json fences.`;
122
+ const { text: retryText } = await generateText({
123
+ model: createModel(props),
124
+ prompt: props.prompt,
125
+ system: errorSystem,
126
+ providerOptions: props.model?.options,
127
+ ...(props.temperature === undefined
128
+ ? {}
129
+ : { temperature: props.temperature }),
130
+ });
131
+ const retryResult = await extractJSONContent(retryText);
132
+ if (retryResult.error) {
133
+ throw new Error(`Failed to generate valid JSON: ${retryResult.error}`);
134
+ }
135
+ content = retryResult.content;
136
+ }
137
+ // Parse JSON to get the object representation
138
+ jsonObject = JSON.parse(content);
139
+ // Use StaticJsonFile to create the file
140
+ const file = await StaticJsonFile("file", props.path, jsonObject);
141
+ jsonContent = file.content;
142
+ }
143
+ // Return the resource
144
+ return this({
145
+ ...props,
146
+ schema: props.schema,
147
+ content: jsonContent,
148
+ json: jsonObject,
149
+ createdAt: Date.now(),
150
+ updatedAt: Date.now(),
151
+ });
152
+ });
153
+ /**
154
+ * Extracts JSON content from between ```json fences
155
+ * Validates that exactly one JSON code block exists
156
+ *
157
+ * @param text The text to extract JSON from
158
+ * @returns The extracted JSON or error message
159
+ */
160
+ async function extractJSONContent(text) {
161
+ const jsonCodeRegex = /```json\s*([\s\S]*?)```/g;
162
+ const matches = Array.from(text.matchAll(jsonCodeRegex));
163
+ if (matches.length === 0) {
164
+ return {
165
+ content: "",
166
+ error: "No JSON code block found in the response. Please include your JSON within ```json fences.",
167
+ };
168
+ }
169
+ if (matches.length > 1) {
170
+ return {
171
+ content: "",
172
+ error: "Multiple JSON code blocks found in the response. Please provide exactly one JSON block within ```json fences.",
173
+ };
174
+ }
175
+ const content = matches[0][1].trim();
176
+ // Validate JSON can be parsed
177
+ try {
178
+ JSON.parse(content);
179
+ return { content };
180
+ }
181
+ catch (e) {
182
+ return {
183
+ content: "",
184
+ error: `Invalid JSON: ${e.message}. Please provide valid JSON syntax.`,
185
+ };
186
+ }
187
+ }
@@ -0,0 +1,122 @@
1
+ import { type CoreMessage } from "ai";
2
+ import type { Context } from "../context";
3
+ import { Resource } from "../resource";
4
+ import type { Secret } from "../secret";
5
+ import { type ModelConfig } from "./client";
6
+ /**
7
+ * Properties for creating or updating a Review
8
+ */
9
+ export interface ReviewProps {
10
+ /**
11
+ * Content to be reviewed
12
+ * Use alchemy template literals to include file context:
13
+ * @example
14
+ * content: await alchemy`
15
+ * Review this code:
16
+ * ${alchemy.file("src/api.ts")}
17
+ * `
18
+ *
19
+ * Required unless messages are provided
20
+ */
21
+ content?: string;
22
+ /**
23
+ * Prompt for the review
24
+ * This should include specific instructions for the review
25
+ * @example
26
+ * prompt: "Review this code for security vulnerabilities, performance issues, and best practices."
27
+ *
28
+ * Required unless messages are provided
29
+ */
30
+ prompt?: string;
31
+ /**
32
+ * Message history for the conversation
33
+ * If provided, this will be used instead of the prompt and content
34
+ */
35
+ messages?: CoreMessage[];
36
+ /**
37
+ * System prompt for the model
38
+ * This is used to provide instructions to the model about how to format the response
39
+ * The default system prompt instructs the model to act as a reviewer
40
+ */
41
+ system?: string;
42
+ /**
43
+ * AI API key to use for generating content
44
+ * If not provided, will use OPENAI_API_KEY environment variable
45
+ */
46
+ apiKey?: Secret;
47
+ /**
48
+ * Model configuration
49
+ */
50
+ model?: ModelConfig;
51
+ /**
52
+ * Temperature for controlling randomness in generation.
53
+ * Higher values (e.g., 0.8) make output more random,
54
+ * lower values (e.g., 0.2) make it more deterministic.
55
+ * @default 0.7
56
+ */
57
+ temperature?: number;
58
+ /**
59
+ * Maximum number of tokens to generate.
60
+ * Higher values allow for longer documents but may increase cost and generation time.
61
+ * @default 10000
62
+ */
63
+ maxTokens?: number;
64
+ }
65
+ /**
66
+ * Output returned after Review creation/update
67
+ */
68
+ export interface Review extends ReviewProps, Resource<"ai::Review"> {
69
+ /**
70
+ * Content of the review
71
+ */
72
+ content: string;
73
+ /**
74
+ * Updated message history with the review response appended
75
+ */
76
+ messages: CoreMessage[];
77
+ /**
78
+ * Time at which the review was created
79
+ */
80
+ createdAt: number;
81
+ /**
82
+ * Time at which the review was last updated
83
+ */
84
+ updatedAt: number;
85
+ }
86
+ /**
87
+ * Creates or updates a Review resource
88
+ *
89
+ * @example
90
+ * // Create a review of a code file
91
+ * const codeReview = await Review("api-review", {
92
+ * content: await alchemy`
93
+ * Review this API implementation:
94
+ * ${alchemy.file("src/api.ts")}
95
+ * `,
96
+ * prompt: "Review this API implementation for security vulnerabilities, performance issues, and best practices. Suggest improvements and alternative approaches."
97
+ * });
98
+ *
99
+ * @example
100
+ * // Create a review of a document
101
+ * const docReview = await Review("tutorial-review", {
102
+ * content: await alchemy`
103
+ * Review this tutorial:
104
+ * ${alchemy.file("docs/tutorials/getting-started.md")}
105
+ * `,
106
+ * prompt: "Review this tutorial for clarity, accuracy, and completeness. Suggest improvements to make it more user-friendly."
107
+ * });
108
+ *
109
+ * @example
110
+ * // Create a review with message history
111
+ * const reviewWithHistory = await Review("code-review-iteration-2", {
112
+ * content: "Updated code implementation",
113
+ * prompt: "Review this updated implementation",
114
+ * messages: [
115
+ * { role: "user", content: "Can you review my code?" },
116
+ * { role: "assistant", content: "I'll review your code. Please share it." },
117
+ * { role: "user", content: "Here's my implementation: [code]" },
118
+ * { role: "assistant", content: "Here's my review of your code: [previous review]" }
119
+ * ]
120
+ * });
121
+ */
122
+ export declare const Review: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | ((this: Context<Review>, id: string, props: ReviewProps) => Promise<Review>);
@@ -0,0 +1,101 @@
1
+ import { generateText } from "ai";
2
+ import { Resource } from "../resource";
3
+ import { createModel, withRateLimitRetry } from "./client";
4
+ /**
5
+ * Default system prompt for review generation
6
+ */
7
+ const DEFAULT_REVIEW_SYSTEM_PROMPT = `You are an expert code reviewer and technical writer. Your task is to provide a thorough review of the provided content. Your review should include:
8
+
9
+ 1. A high-level summary of the content
10
+ 2. Detailed feedback on the content's quality, accuracy, and completeness
11
+ 3. Specific suggestions for improvement
12
+ 4. Alternative approaches or solutions where applicable
13
+ 5. Best practices that should be followed
14
+ 6. Potential issues or concerns that should be addressed
15
+
16
+ Provide your review in a clear, well-structured format.`;
17
+ /**
18
+ * Creates or updates a Review resource
19
+ *
20
+ * @example
21
+ * // Create a review of a code file
22
+ * const codeReview = await Review("api-review", {
23
+ * content: await alchemy`
24
+ * Review this API implementation:
25
+ * ${alchemy.file("src/api.ts")}
26
+ * `,
27
+ * prompt: "Review this API implementation for security vulnerabilities, performance issues, and best practices. Suggest improvements and alternative approaches."
28
+ * });
29
+ *
30
+ * @example
31
+ * // Create a review of a document
32
+ * const docReview = await Review("tutorial-review", {
33
+ * content: await alchemy`
34
+ * Review this tutorial:
35
+ * ${alchemy.file("docs/tutorials/getting-started.md")}
36
+ * `,
37
+ * prompt: "Review this tutorial for clarity, accuracy, and completeness. Suggest improvements to make it more user-friendly."
38
+ * });
39
+ *
40
+ * @example
41
+ * // Create a review with message history
42
+ * const reviewWithHistory = await Review("code-review-iteration-2", {
43
+ * content: "Updated code implementation",
44
+ * prompt: "Review this updated implementation",
45
+ * messages: [
46
+ * { role: "user", content: "Can you review my code?" },
47
+ * { role: "assistant", content: "I'll review your code. Please share it." },
48
+ * { role: "user", content: "Here's my implementation: [code]" },
49
+ * { role: "assistant", content: "Here's my review of your code: [previous review]" }
50
+ * ]
51
+ * });
52
+ */
53
+ export const Review = Resource("ai::Review", async function (id, props) {
54
+ if (this.phase === "delete") {
55
+ return this.destroy();
56
+ }
57
+ // Use provided system prompt or default
58
+ const system = props.system || DEFAULT_REVIEW_SYSTEM_PROMPT;
59
+ // Create messages array if not provided
60
+ const messages = props.messages ||
61
+ (props.content && props.prompt
62
+ ? [
63
+ {
64
+ role: "user",
65
+ content: `${props.content}\n\n${props.prompt}`,
66
+ },
67
+ ]
68
+ : []);
69
+ if (messages.length === 0) {
70
+ throw new Error("Either messages or both content and prompt must be provided");
71
+ }
72
+ // Generate review content with rate limit retry
73
+ const { text } = await withRateLimitRetry(async () => {
74
+ return generateText({
75
+ model: createModel(props),
76
+ messages,
77
+ system,
78
+ maxTokens: props.maxTokens || 8192,
79
+ providerOptions: props.model?.options,
80
+ ...(props.temperature === undefined
81
+ ? {}
82
+ : { temperature: props.temperature }),
83
+ });
84
+ });
85
+ // Create updated message history with the AI's response
86
+ const updatedMessages = [
87
+ ...messages,
88
+ {
89
+ role: "assistant",
90
+ content: text,
91
+ },
92
+ ];
93
+ // Return the review with the direct text from the LLM and updated messages
94
+ return this({
95
+ ...props,
96
+ content: text,
97
+ messages: updatedMessages,
98
+ createdAt: Date.now(),
99
+ updatedAt: Date.now(),
100
+ });
101
+ });