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,131 @@
1
+ import type { Context } from "../context";
2
+ import { Resource } from "../resource";
3
+ import type { Secret } from "../secret";
4
+ import { type ModelConfig } from "./client";
5
+ /**
6
+ * Properties for creating or updating a CSSFile
7
+ */
8
+ export interface CSSFileProps {
9
+ /**
10
+ * Path to the CSS file
11
+ */
12
+ path: string;
13
+ /**
14
+ * Base URL for the OpenAI API
15
+ * @default 'https://api.openai.com/v1'
16
+ */
17
+ baseURL?: string;
18
+ /**
19
+ * Prompt for generating content
20
+ * Use alchemy template literals to include file context:
21
+ * @example
22
+ * prompt: await alchemy`
23
+ * Generate CSS styles for:
24
+ * ${alchemy.file("src/components/Button.jsx")}
25
+ * `
26
+ */
27
+ prompt: string;
28
+ /**
29
+ * System prompt for the model
30
+ * This is used to provide instructions to the model about how to format the response
31
+ * The default system prompt instructs the model to return CSS code inside ```css fences
32
+ * @default "You are a CSS code generator. Create CSS code based on the user's requirements. Your response MUST include only CSS code inside ```css fences. Do not include any other text, explanations, or multiple code blocks."
33
+ */
34
+ system?: string;
35
+ /**
36
+ * OpenAI API key to use for generating content
37
+ * If not provided, will use OPENAI_API_KEY environment variable
38
+ */
39
+ apiKey?: Secret;
40
+ /**
41
+ * Model configuration
42
+ */
43
+ model?: ModelConfig;
44
+ /**
45
+ * Temperature for controlling randomness in generation.
46
+ * Higher values (e.g., 0.8) make output more random,
47
+ * lower values (e.g., 0.2) make it more deterministic.
48
+ * @default 0.7
49
+ */
50
+ temperature?: number;
51
+ }
52
+ /**
53
+ * A CSS file that can be created, updated, and deleted
54
+ */
55
+ export interface CSSFile extends CSSFileProps, Resource<"ai::CSSFile"> {
56
+ /**
57
+ * Content of the CSS file
58
+ */
59
+ content: string;
60
+ /**
61
+ * Time at which the file was created
62
+ */
63
+ createdAt: number;
64
+ /**
65
+ * Time at which the file was last updated
66
+ */
67
+ updatedAt: number;
68
+ }
69
+ /**
70
+ * Resource for generating CSS files using AI models.
71
+ * Extracts CSS code from between ```css fences and validates the response.
72
+ *
73
+ * @example
74
+ * // Create styles for a website
75
+ * const mainStyles = await CSSFile("main-styles", {
76
+ * path: "./public/css/main.css",
77
+ * prompt: await alchemy`
78
+ * Generate modern CSS styles for a company website with:
79
+ * - Clean, minimalist design
80
+ * - Primary color: #0062ff
81
+ * - Secondary color: #6c757d
82
+ * - Light gray background
83
+ * - Responsive layout for mobile, tablet, and desktop
84
+ * - Custom styles for buttons, cards, and navigation
85
+ * `,
86
+ * model: {
87
+ * id: "gpt-4o",
88
+ * provider: "openai"
89
+ * }
90
+ * });
91
+ *
92
+ * @example
93
+ * // Generate CSS based on existing HTML
94
+ * const componentStyles = await CSSFile("component-styles", {
95
+ * path: "./src/styles/component.css",
96
+ * prompt: await alchemy`
97
+ * Create CSS styles for this HTML component:
98
+ * ${alchemy.file("src/components/Card.html")}
99
+ *
100
+ * The styles should be:
101
+ * - Modern and clean
102
+ * - Include hover effects and transitions
103
+ * - Support both light and dark themes
104
+ * - Use CSS variables for colors and spacing
105
+ * `,
106
+ * temperature: 0.2
107
+ * });
108
+ *
109
+ * @example
110
+ * // Generate CSS animation with custom system prompt
111
+ * const animationStyles = await CSSFile("animations", {
112
+ * path: "./src/styles/animations.css",
113
+ * prompt: await alchemy`
114
+ * Create CSS animations for:
115
+ * - Fade in/out
116
+ * - Slide in from different directions
117
+ * - Pulse effect
118
+ * - Bounce effect
119
+ * - Scale in/out
120
+ * - Rotate
121
+ *
122
+ * Each animation should be reusable via class names.
123
+ * `,
124
+ * system: "You are an expert CSS animator. Create a single CSS file inside ```css fences with no additional text. Use modern CSS animation techniques and include vendor prefixes where needed for browser compatibility.",
125
+ * model: {
126
+ * id: "claude-3-opus-20240229",
127
+ * provider: "anthropic"
128
+ * }
129
+ * });
130
+ */
131
+ export declare const CSSFile: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | ((this: Context<CSSFile>, id: string, props: CSSFileProps) => Promise<CSSFile>);
@@ -0,0 +1,141 @@
1
+ import { generateText } from "ai";
2
+ import { StaticCSSFile } from "../fs/static-css-file";
3
+ import { Resource } from "../resource";
4
+ import { createModel } from "./client";
5
+ /**
6
+ * Default system prompt for CSS file generation
7
+ */
8
+ const DEFAULT_CSS_SYSTEM_PROMPT = "You are a CSS code generator. Create CSS code based on the user's requirements. Your response MUST include only CSS code inside ```css fences. Do not include any other text, explanations, or multiple code blocks.";
9
+ /**
10
+ * Resource for generating CSS files using AI models.
11
+ * Extracts CSS code from between ```css fences and validates the response.
12
+ *
13
+ * @example
14
+ * // Create styles for a website
15
+ * const mainStyles = await CSSFile("main-styles", {
16
+ * path: "./public/css/main.css",
17
+ * prompt: await alchemy`
18
+ * Generate modern CSS styles for a company website with:
19
+ * - Clean, minimalist design
20
+ * - Primary color: #0062ff
21
+ * - Secondary color: #6c757d
22
+ * - Light gray background
23
+ * - Responsive layout for mobile, tablet, and desktop
24
+ * - Custom styles for buttons, cards, and navigation
25
+ * `,
26
+ * model: {
27
+ * id: "gpt-4o",
28
+ * provider: "openai"
29
+ * }
30
+ * });
31
+ *
32
+ * @example
33
+ * // Generate CSS based on existing HTML
34
+ * const componentStyles = await CSSFile("component-styles", {
35
+ * path: "./src/styles/component.css",
36
+ * prompt: await alchemy`
37
+ * Create CSS styles for this HTML component:
38
+ * ${alchemy.file("src/components/Card.html")}
39
+ *
40
+ * The styles should be:
41
+ * - Modern and clean
42
+ * - Include hover effects and transitions
43
+ * - Support both light and dark themes
44
+ * - Use CSS variables for colors and spacing
45
+ * `,
46
+ * temperature: 0.2
47
+ * });
48
+ *
49
+ * @example
50
+ * // Generate CSS animation with custom system prompt
51
+ * const animationStyles = await CSSFile("animations", {
52
+ * path: "./src/styles/animations.css",
53
+ * prompt: await alchemy`
54
+ * Create CSS animations for:
55
+ * - Fade in/out
56
+ * - Slide in from different directions
57
+ * - Pulse effect
58
+ * - Bounce effect
59
+ * - Scale in/out
60
+ * - Rotate
61
+ *
62
+ * Each animation should be reusable via class names.
63
+ * `,
64
+ * system: "You are an expert CSS animator. Create a single CSS file inside ```css fences with no additional text. Use modern CSS animation techniques and include vendor prefixes where needed for browser compatibility.",
65
+ * model: {
66
+ * id: "claude-3-opus-20240229",
67
+ * provider: "anthropic"
68
+ * }
69
+ * });
70
+ */
71
+ export const CSSFile = Resource("ai::CSSFile", async function (id, props) {
72
+ // Handle deletion phase
73
+ if (this.phase === "delete") {
74
+ return this.destroy();
75
+ }
76
+ // Use provided system prompt or default
77
+ const system = props.system || DEFAULT_CSS_SYSTEM_PROMPT;
78
+ // Generate initial content
79
+ const { text } = await generateText({
80
+ model: createModel(props),
81
+ prompt: props.prompt,
82
+ system,
83
+ providerOptions: props.model?.options,
84
+ ...(props.temperature === undefined
85
+ ? {}
86
+ : { temperature: props.temperature }),
87
+ });
88
+ // Extract and validate CSS code
89
+ let { code, error } = await extractCSSCode(text);
90
+ // Re-prompt if there are validation errors
91
+ if (error) {
92
+ const errorSystem = `${system}\n\nERROR: ${error}\n\nPlease try again and ensure your response contains exactly one CSS code block inside \`\`\`css fences.`;
93
+ const { text: retryText } = await generateText({
94
+ model: createModel(props),
95
+ prompt: props.prompt,
96
+ system: errorSystem,
97
+ providerOptions: props.model?.options,
98
+ ...(props.temperature === undefined
99
+ ? {}
100
+ : { temperature: props.temperature }),
101
+ });
102
+ const retryResult = await extractCSSCode(retryText);
103
+ if (retryResult.error) {
104
+ throw new Error(`Failed to generate valid CSS code: ${retryResult.error}\n${retryText}`);
105
+ }
106
+ code = retryResult.code;
107
+ }
108
+ // Use StaticCSSFile to create/update the file
109
+ const file = await StaticCSSFile("file", props.path, code);
110
+ // Return the resource
111
+ return this({
112
+ ...props,
113
+ content: file.content,
114
+ createdAt: Date.now(),
115
+ updatedAt: Date.now(),
116
+ });
117
+ });
118
+ /**
119
+ * Extracts CSS code from between ```css fences
120
+ * Validates that exactly one CSS code block exists
121
+ *
122
+ * @param text The text to extract CSS code from
123
+ * @returns The extracted CSS code or error message
124
+ */
125
+ async function extractCSSCode(text) {
126
+ const cssCodeRegex = /```css\s*([\s\S]*?)```/g;
127
+ const matches = Array.from(text.matchAll(cssCodeRegex));
128
+ if (matches.length === 0) {
129
+ return {
130
+ code: "",
131
+ error: "No CSS code block found in the response. Please include your code within ```css fences.",
132
+ };
133
+ }
134
+ if (matches.length > 1) {
135
+ return {
136
+ code: "",
137
+ error: "Multiple CSS code blocks found in the response. Please provide exactly one code block within ```css fences.",
138
+ };
139
+ }
140
+ return { code: matches[0][1].trim() };
141
+ }
package/lib/ai/data.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { type CoreMessage } from "ai";
1
2
  import type { JsonSchema, Type, type } from "arktype";
2
3
  import type { Context } from "../context";
3
4
  import { Resource } from "../resource";
@@ -20,13 +21,25 @@ export interface DataProps<T extends Type<any, any>> {
20
21
  * ${alchemy.file("src/data.ts")}
21
22
  * `
22
23
  */
23
- prompt: string;
24
+ prompt?: string;
25
+ /**
26
+ * Message history for the conversation
27
+ * If provided, this will be used instead of the prompt
28
+ */
29
+ messages?: CoreMessage[];
24
30
  /**
25
31
  * System prompt to guide the AI's behavior
26
32
  * @example
27
33
  * system: "You are a technical writer tasked with describing code"
28
34
  */
29
35
  system?: string;
36
+ /**
37
+ * Temperature for controlling randomness in generation.
38
+ * Higher values (e.g., 0.8) make output more random,
39
+ * lower values (e.g., 0.2) make it more deterministic.
40
+ * @default 0.7
41
+ */
42
+ temperature?: number;
30
43
  /**
31
44
  * Base URL for the OpenAI API
32
45
  * @default 'https://api.openai.com/v1'
@@ -51,6 +64,10 @@ export interface Data<T> extends Resource<"ai::Object"> {
51
64
  * The generated content, typed according to the provided schema
52
65
  */
53
66
  object: T;
67
+ /**
68
+ * Updated message history with the AI's response appended
69
+ */
70
+ messages: CoreMessage[];
54
71
  /**
55
72
  * Time at which the content was generated
56
73
  */
@@ -69,13 +86,20 @@ export interface Data<T> extends Resource<"ai::Object"> {
69
86
  * price: "number"
70
87
  * });
71
88
  *
72
- * const product = await Object("new-product", {
89
+ * const product = await Data("new-product", {
73
90
  * schema: productSchema,
74
91
  * prompt: "Generate a product description for a new smartphone",
75
- * system: "You are a product copywriter specializing in tech products"
92
+ * system: "You are a product copywriter specializing in tech products",
93
+ * model: {
94
+ * id: "gpt-4o",
95
+ * provider: "openai",
96
+ * options: {
97
+ * temperature: 0.7
98
+ * }
99
+ * }
76
100
  * });
77
101
  *
78
- * console.log(product.content); // Typed as per schema
102
+ * console.log(product.object); // Typed as per schema
79
103
  *
80
104
  * @example
81
105
  * // Generate code documentation with context
@@ -89,13 +113,34 @@ export interface Data<T> extends Resource<"ai::Object"> {
89
113
  * returns: "string"
90
114
  * });
91
115
  *
92
- * const docs = await Object("function-docs", {
116
+ * const docs = await Data("function-docs", {
93
117
  * schema: docSchema,
94
118
  * prompt: await alchemy`
95
119
  * Generate documentation for this function:
96
120
  * ${alchemy.file("src/utils/format.ts")}
97
121
  * `,
98
- * system: "You are a technical documentation writer"
122
+ * system: "You are a technical documentation writer",
123
+ * temperature: 0.2
124
+ * });
125
+ *
126
+ * @example
127
+ * // Using message history for iterative generation
128
+ * const feedbackSchema = type({
129
+ * rating: "number",
130
+ * positives: "string[]",
131
+ * improvements: "string[]",
132
+ * summary: "string"
133
+ * });
134
+ *
135
+ * const feedback = await Data("product-feedback", {
136
+ * schema: feedbackSchema,
137
+ * messages: [
138
+ * { role: "user", content: "I'd like feedback on my product design" },
139
+ * { role: "assistant", content: "I'd be happy to provide feedback. What's your product?" },
140
+ * { role: "user", content: "It's a new smart home device that..." }
141
+ * ],
142
+ * system: "You are a product design expert providing structured feedback",
143
+ * temperature: 0.3
99
144
  * });
100
145
  */
101
146
  export declare const Data: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | (<const T extends Type<any, any>>(this: Context<Data<any>>, id: string, props: DataProps<T>) => Promise<Data<type.infer<T>>>);
package/lib/ai/data.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { generateObject } from "ai";
2
2
  import { Resource } from "../resource";
3
3
  import { ark } from "./ark";
4
- import { createClient, getModelId, getModelOptions, } from "./client";
4
+ import { createModel } from "./client";
5
5
  /**
6
6
  * Resource for generating structured content using the Vercel AI SDK.
7
7
  * Supports powerful context handling through the alchemy template literal tag.
@@ -15,13 +15,20 @@ import { createClient, getModelId, getModelOptions, } from "./client";
15
15
  * price: "number"
16
16
  * });
17
17
  *
18
- * const product = await Object("new-product", {
18
+ * const product = await Data("new-product", {
19
19
  * schema: productSchema,
20
20
  * prompt: "Generate a product description for a new smartphone",
21
- * system: "You are a product copywriter specializing in tech products"
21
+ * system: "You are a product copywriter specializing in tech products",
22
+ * model: {
23
+ * id: "gpt-4o",
24
+ * provider: "openai",
25
+ * options: {
26
+ * temperature: 0.7
27
+ * }
28
+ * }
22
29
  * });
23
30
  *
24
- * console.log(product.content); // Typed as per schema
31
+ * console.log(product.object); // Typed as per schema
25
32
  *
26
33
  * @example
27
34
  * // Generate code documentation with context
@@ -35,36 +42,75 @@ import { createClient, getModelId, getModelOptions, } from "./client";
35
42
  * returns: "string"
36
43
  * });
37
44
  *
38
- * const docs = await Object("function-docs", {
45
+ * const docs = await Data("function-docs", {
39
46
  * schema: docSchema,
40
47
  * prompt: await alchemy`
41
48
  * Generate documentation for this function:
42
49
  * ${alchemy.file("src/utils/format.ts")}
43
50
  * `,
44
- * system: "You are a technical documentation writer"
51
+ * system: "You are a technical documentation writer",
52
+ * temperature: 0.2
53
+ * });
54
+ *
55
+ * @example
56
+ * // Using message history for iterative generation
57
+ * const feedbackSchema = type({
58
+ * rating: "number",
59
+ * positives: "string[]",
60
+ * improvements: "string[]",
61
+ * summary: "string"
62
+ * });
63
+ *
64
+ * const feedback = await Data("product-feedback", {
65
+ * schema: feedbackSchema,
66
+ * messages: [
67
+ * { role: "user", content: "I'd like feedback on my product design" },
68
+ * { role: "assistant", content: "I'd be happy to provide feedback. What's your product?" },
69
+ * { role: "user", content: "It's a new smart home device that..." }
70
+ * ],
71
+ * system: "You are a product design expert providing structured feedback",
72
+ * temperature: 0.3
45
73
  * });
46
74
  */
47
75
  export const Data = Resource("ai::Object", async function (id, props) {
48
76
  if (this.phase === "delete") {
49
77
  return this.destroy();
50
78
  }
51
- // Initialize OpenAI compatible provider using shared client
52
- const provider = createClient(props);
79
+ // Validate that either prompt or messages is provided
80
+ if (!props.prompt && !props.messages) {
81
+ throw new Error("Either prompt or messages must be provided");
82
+ }
83
+ // Create messages array if only prompt is provided
84
+ const messages = props.messages || [{ role: "user", content: props.prompt }];
53
85
  // Generate structured output using generateObject
54
86
  const { object } = await generateObject({
55
- model: provider(getModelId(props)),
87
+ model: createModel(props),
56
88
  // Convert ArkType schema to Zod schema for generateObject
57
89
  // This is needed because generateObject expects a Zod schema
58
90
  schema: ark.schema(props.schema),
91
+ providerOptions: props.model?.options,
59
92
  system: props.system ||
60
93
  "You are an AI assistant tasked with generating structured content.",
61
- prompt: props.prompt,
62
- ...getModelOptions(props),
94
+ messages,
95
+ ...(props.temperature === undefined
96
+ ? {}
97
+ : // some models error if you provide it (rather than ignoring it)
98
+ { temperature: props.temperature }),
63
99
  });
64
- // Return the resource with typed content
100
+ // Create updated message history with the structured response
101
+ const responseText = JSON.stringify(object);
102
+ const updatedMessages = [
103
+ ...messages,
104
+ {
105
+ role: "assistant",
106
+ content: responseText,
107
+ },
108
+ ];
109
+ // Return the resource with typed content and updated messages
65
110
  return this({
66
111
  type: props.schema,
67
112
  object: object,
113
+ messages: updatedMessages,
68
114
  createdAt: Date.now(),
69
115
  });
70
116
  });
@@ -1,4 +1,6 @@
1
+ import { type CoreMessage } from "ai";
1
2
  import type { Context } from "../context";
3
+ import { StaticTextFile } from "../fs/static-text-file";
2
4
  import { Resource } from "../resource";
3
5
  import type { Secret } from "../secret";
4
6
  import { type ModelConfig } from "./client";
@@ -11,9 +13,10 @@ export interface DocumentProps {
11
13
  */
12
14
  title: string;
13
15
  /**
14
- * Path to the markdown document
16
+ * Optional path to the markdown document
17
+ * If provided, document will be written to this path
15
18
  */
16
- path: string;
19
+ path?: string;
17
20
  /**
18
21
  * Base URL for the OpenAI API
19
22
  * @default 'https://api.openai.com/v1'
@@ -28,7 +31,25 @@ export interface DocumentProps {
28
31
  * ${alchemy.file("src/api.ts")}
29
32
  * `
30
33
  */
31
- prompt: string;
34
+ prompt?: string;
35
+ /**
36
+ * Message history for conversation-based generation
37
+ * If provided, this will be used instead of the prompt
38
+ * @example
39
+ * messages: [
40
+ * { role: "user", content: "Generate API documentation for this file" },
41
+ * { role: "assistant", content: "I'll create detailed API docs. What file should I document?" },
42
+ * { role: "user", content: "Please document src/api.ts" }
43
+ * ]
44
+ */
45
+ messages?: CoreMessage[];
46
+ /**
47
+ * System prompt for the model
48
+ * This is used to provide instructions to the model about how to format the response
49
+ * The default system prompt instructs the model to return a single markdown document inside ```md fences
50
+ * @default "You are a technical documentation writer. Create a single markdown document based on the user's requirements. Your response MUST include only a single markdown document inside ```md fences. Do not include any other text, explanations, or multiple code blocks."
51
+ */
52
+ system?: string;
32
53
  /**
33
54
  * OpenAI API key to use for generating content
34
55
  * If not provided, will use OPENAI_API_KEY environment variable
@@ -38,6 +59,19 @@ export interface DocumentProps {
38
59
  * Model configuration
39
60
  */
40
61
  model?: ModelConfig;
62
+ /**
63
+ * Temperature for controlling randomness in generation.
64
+ * Higher values (e.g., 0.8) make output more random,
65
+ * lower values (e.g., 0.2) make it more deterministic.
66
+ * @default 0.7
67
+ */
68
+ temperature?: number;
69
+ /**
70
+ * Maximum number of tokens to generate.
71
+ * Higher values allow for longer documents but may increase cost and generation time.
72
+ * @default 10000
73
+ */
74
+ maxTokens?: number;
41
75
  }
42
76
  /**
43
77
  * A markdown document that can be created, updated, and deleted
@@ -47,6 +81,10 @@ export interface Document extends DocumentProps, Resource<"docs::Document"> {
47
81
  * Content of the document
48
82
  */
49
83
  content: string;
84
+ /**
85
+ * Updated message history with the document response appended
86
+ */
87
+ messages: CoreMessage[];
50
88
  /**
51
89
  * Time at which the document was created
52
90
  */
@@ -55,47 +93,93 @@ export interface Document extends DocumentProps, Resource<"docs::Document"> {
55
93
  * Time at which the document was last updated
56
94
  */
57
95
  updatedAt: number;
96
+ /**
97
+ * File resource if path was provided
98
+ */
99
+ file?: StaticTextFile;
58
100
  }
59
101
  /**
60
102
  * Resource for managing AI-generated markdown documents using the Vercel AI SDK.
61
103
  * Supports powerful context handling through the alchemy template literal tag.
62
104
  *
63
105
  * @example
64
- * // Create a document using alchemy template literals for context
106
+ * // Create an in-memory document (no file created)
107
+ * const apiDocs = await Document("api-docs", {
108
+ * title: "API Documentation",
109
+ * prompt: await alchemy`
110
+ * Generate API documentation based on these source files:
111
+ * ${alchemy.file("src/api.ts")}
112
+ * ${alchemy.file("src/types.ts")}
113
+ * `,
114
+ * model: {
115
+ * id: "gpt-4o",
116
+ * provider: "openai"
117
+ * }
118
+ * });
119
+ *
120
+ * @example
121
+ * // Create a document and write it to disk
65
122
  * const apiDocs = await Document("api-docs", {
123
+ * title: "API Documentation",
66
124
  * path: "./docs/api.md",
67
125
  * prompt: await alchemy`
68
126
  * Generate API documentation based on these source files:
69
127
  * ${alchemy.file("src/api.ts")}
70
128
  * ${alchemy.file("src/types.ts")}
71
- * `
72
- * // The above will automatically append the file contents as code blocks:
73
- * //
74
- * // Generate API documentation based on these source files:
75
- * // [api.ts](src/api.ts)
76
- * // [types.ts](src/types.ts)
77
- * //
78
- * // // src/api.ts
79
- * // ```ts
80
- * // ... contents of api.ts ...
81
- * // ```
82
- * //
83
- * // // src/types.ts
84
- * // ```ts
85
- * // ... contents of types.ts ...
86
- * // ```
129
+ * `,
130
+ * model: {
131
+ * id: "gpt-4o",
132
+ * provider: "openai"
133
+ * }
87
134
  * });
88
135
  *
89
136
  * @example
90
- * // Use alchemy template literals with file collections
137
+ * // Use message history for iterative document generation
138
+ * const apiDocs = await Document("api-docs", {
139
+ * title: "API Documentation",
140
+ * path: "./docs/api.md",
141
+ * messages: [
142
+ * { role: "user", content: "Create API documentation for these files" },
143
+ * { role: "assistant", content: "I'll help you create API documentation. Please provide the files." },
144
+ * { role: "user", content: "Here are the files: [file contents]" }
145
+ * ],
146
+ * system: "You are a technical documentation writer. Generate clear and concise API documentation.",
147
+ * model: {
148
+ * id: "gpt-4o",
149
+ * provider: "openai"
150
+ * }
151
+ * });
152
+ *
153
+ * @example
154
+ * // Use alchemy template literals with file collections and temperature control
91
155
  * const modelDocs = await Document("models", {
156
+ * title: "Data Models",
92
157
  * path: "./docs/models.md",
93
158
  * prompt: await alchemy`
94
159
  * Write documentation for these data models:
95
160
  * ${alchemy.files("src/models/user.ts", "src/models/post.ts")}
96
- * `
97
- * // This creates a prompt with all files appended as code blocks,
98
- * // automatically handling syntax highlighting based on file extensions
161
+ * `,
162
+ * temperature: 0.2 // Lower temperature for more deterministic output
163
+ * });
164
+ *
165
+ * @example
166
+ * // Advanced model configuration with custom provider options and custom system prompt
167
+ * const techDocs = await Document("tech-specs", {
168
+ * title: "Technical Specifications",
169
+ * path: "./docs/tech-specs.md",
170
+ * prompt: await alchemy`
171
+ * Create detailed technical specifications based on these requirements:
172
+ * ${alchemy.file("requirements/system.md")}
173
+ * `,
174
+ * system: "You are an expert technical writer specializing in system specifications. Create a single markdown document inside ```md fences with no additional text.",
175
+ * model: {
176
+ * id: "o3-mini",
177
+ * provider: "openai",
178
+ * options: {
179
+ * reasoningEffort: "high"
180
+ * }
181
+ * },
182
+ * temperature: 0.1
99
183
  * });
100
184
  */
101
185
  export declare const Document: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | ((this: Context<Document>, id: string, props: DocumentProps) => Promise<Document>);