synthos 0.1.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.
Files changed (102) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +71 -0
  3. package/bin/synthos.js +3 -0
  4. package/default-pages/[application].html +87 -0
  5. package/default-pages/[markdown].html +261 -0
  6. package/default-pages/[sidebar].html +89 -0
  7. package/default-pages/[split-application].html +133 -0
  8. package/default-pages/json_tools.html +176 -0
  9. package/default-scripts/android.terminal.json +7 -0
  10. package/default-scripts/linux-terminal.json +7 -0
  11. package/default-scripts/mac-terminal.json +7 -0
  12. package/default-scripts/windows-terminal.json +7 -0
  13. package/dist/files.d.ts +9 -0
  14. package/dist/files.d.ts.map +1 -0
  15. package/dist/files.js +79 -0
  16. package/dist/files.js.map +1 -0
  17. package/dist/index.d.ts +7 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +23 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/init.d.ts +9 -0
  22. package/dist/init.d.ts.map +1 -0
  23. package/dist/init.js +59 -0
  24. package/dist/init.js.map +1 -0
  25. package/dist/pages.d.ts +6 -0
  26. package/dist/pages.d.ts.map +1 -0
  27. package/dist/pages.js +55 -0
  28. package/dist/pages.js.map +1 -0
  29. package/dist/scripts.d.ts +14 -0
  30. package/dist/scripts.d.ts.map +1 -0
  31. package/dist/scripts.js +103 -0
  32. package/dist/scripts.js.map +1 -0
  33. package/dist/service/createCompletePrompt.d.ts +4 -0
  34. package/dist/service/createCompletePrompt.d.ts.map +1 -0
  35. package/dist/service/createCompletePrompt.js +42 -0
  36. package/dist/service/createCompletePrompt.js.map +1 -0
  37. package/dist/service/generateImage.d.ts +32 -0
  38. package/dist/service/generateImage.d.ts.map +1 -0
  39. package/dist/service/generateImage.js +71 -0
  40. package/dist/service/generateImage.js.map +1 -0
  41. package/dist/service/index.d.ts +8 -0
  42. package/dist/service/index.d.ts.map +1 -0
  43. package/dist/service/index.js +24 -0
  44. package/dist/service/index.js.map +1 -0
  45. package/dist/service/requiresSettings.d.ts +3 -0
  46. package/dist/service/requiresSettings.d.ts.map +1 -0
  47. package/dist/service/requiresSettings.js +24 -0
  48. package/dist/service/requiresSettings.js.map +1 -0
  49. package/dist/service/server.d.ts +4 -0
  50. package/dist/service/server.d.ts.map +1 -0
  51. package/dist/service/server.js +26 -0
  52. package/dist/service/server.js.map +1 -0
  53. package/dist/service/transformPage.d.ts +11 -0
  54. package/dist/service/transformPage.d.ts.map +1 -0
  55. package/dist/service/transformPage.js +119 -0
  56. package/dist/service/transformPage.js.map +1 -0
  57. package/dist/service/useApiRoutes.d.ts +4 -0
  58. package/dist/service/useApiRoutes.d.ts.map +1 -0
  59. package/dist/service/useApiRoutes.js +95 -0
  60. package/dist/service/useApiRoutes.js.map +1 -0
  61. package/dist/service/useDataRoutes.d.ts +4 -0
  62. package/dist/service/useDataRoutes.d.ts.map +1 -0
  63. package/dist/service/useDataRoutes.js +98 -0
  64. package/dist/service/useDataRoutes.js.map +1 -0
  65. package/dist/service/usePageRoutes.d.ts +5 -0
  66. package/dist/service/usePageRoutes.d.ts.map +1 -0
  67. package/dist/service/usePageRoutes.js +132 -0
  68. package/dist/service/usePageRoutes.js.map +1 -0
  69. package/dist/settings.d.ts +13 -0
  70. package/dist/settings.d.ts.map +1 -0
  71. package/dist/settings.js +55 -0
  72. package/dist/settings.js.map +1 -0
  73. package/dist/synthos-cli.d.ts +2 -0
  74. package/dist/synthos-cli.d.ts.map +1 -0
  75. package/dist/synthos-cli.js +43 -0
  76. package/dist/synthos-cli.js.map +1 -0
  77. package/images/home.png +0 -0
  78. package/images/page-management.png +0 -0
  79. package/images/settings.png +0 -0
  80. package/images/synthos-square.png +0 -0
  81. package/package.json +58 -0
  82. package/required-pages/apis.html +347 -0
  83. package/required-pages/home.html +83 -0
  84. package/required-pages/pages.html +135 -0
  85. package/required-pages/scripts.html +335 -0
  86. package/required-pages/settings.html +158 -0
  87. package/src/files.ts +49 -0
  88. package/src/index.ts +6 -0
  89. package/src/init.ts +66 -0
  90. package/src/pages.ts +55 -0
  91. package/src/scripts.ts +130 -0
  92. package/src/service/createCompletePrompt.ts +40 -0
  93. package/src/service/generateImage.ts +101 -0
  94. package/src/service/index.ts +7 -0
  95. package/src/service/requiresSettings.ts +22 -0
  96. package/src/service/server.ts +26 -0
  97. package/src/service/transformPage.ts +135 -0
  98. package/src/service/useApiRoutes.ts +95 -0
  99. package/src/service/useDataRoutes.ts +97 -0
  100. package/src/service/usePageRoutes.ts +147 -0
  101. package/src/settings.ts +60 -0
  102. package/src/synthos-cli.ts +38 -0
package/src/pages.ts ADDED
@@ -0,0 +1,55 @@
1
+ import {checkIfExists, listFiles, loadFile, saveFile} from './files';
2
+ import path from 'path';
3
+
4
+ // Page State Cache
5
+ const _pages: { [name: string]: string } = {};
6
+
7
+ export async function listPages(pagesFolder: string, fallbackPagesFolder: string): Promise<string[]> {
8
+ // Load all pages from primary pages folder
9
+ const all = (await listFiles(pagesFolder)).filter(file => file.endsWith('.html'));
10
+
11
+ // Add pages from fallback pages folder that don't already exist
12
+ (await listFiles(fallbackPagesFolder)).forEach(file => {
13
+ if (file.endsWith('.html') && !all.includes(file)) {
14
+ all.push(file);
15
+ }
16
+ });
17
+
18
+ // Remove .html and sort all files
19
+ const sorted = all.map(file => file.substring(0, file.length - 5)).sort();
20
+
21
+ // Move [templates] to end of array
22
+ const pages = sorted.filter(page => !page.startsWith('['));
23
+ const templates = sorted.filter(page => page.startsWith('['));
24
+ pages.push(...templates);
25
+
26
+ return pages;
27
+ }
28
+
29
+ export async function loadPageState(pagesFolder: string, name: string, reset: boolean): Promise<string|undefined> {
30
+ if (!_pages[name] || reset) {
31
+ // Check for file to exist
32
+ const filename = path.join(pagesFolder, `${name}.html`);
33
+ if (!(await checkIfExists(filename))) {
34
+ return undefined;
35
+ }
36
+
37
+ // Load file
38
+ _pages[name] = await loadFile(filename);
39
+ }
40
+
41
+ return _pages[name];
42
+ }
43
+
44
+ export function normalizePageName(name: string|undefined): string|undefined {
45
+ return typeof name == 'string' && name.length > 0 ? name.replace(/[^a-z0-9\-_\[\]\(\)\{\}@#\$%&]/gi, '_').toLowerCase() : undefined;
46
+ }
47
+
48
+ export async function savePageState(pagesFolder: string, name: string, content: string): Promise<void> {
49
+ _pages[name] = content;
50
+ await saveFile(path.join(pagesFolder, `${name}.html`), content);
51
+ }
52
+
53
+ export function updatePageState(name: string, content: string): void {
54
+ _pages[name] = content;
55
+ }
package/src/scripts.ts ADDED
@@ -0,0 +1,130 @@
1
+ import { AgentCompletion, variableToString } from 'agentm-core';
2
+ import { spawn } from 'child_process';
3
+ import path from 'path';
4
+ import { loadFile, listFiles, checkIfExists } from './files';
5
+
6
+ export interface ExecuteScriptArgs {
7
+ pagesFolder: string;
8
+ scriptId: string;
9
+ variables: Record<string, string>;
10
+ }
11
+
12
+ export interface ExecuteScriptResponse {
13
+ output: string;
14
+ errors: string[];
15
+ }
16
+
17
+ export async function listScripts(pagesFolder: string): Promise<string> {
18
+ if (!scriptsList) {
19
+ let list: string[] = [];
20
+ const folder = path.join(pagesFolder, 'scripts');
21
+ if (await checkIfExists(folder)) {
22
+ const files = (await listFiles(folder)).filter(f => f.endsWith('.json'));
23
+ for (const file of files) {
24
+ const script = await loadFile(path.join(folder, file));
25
+ if (script) {
26
+ try {
27
+ const parsed = JSON.parse(script) as ParsedScript;
28
+ const description = parsed.description ? `description: ${parsed.description}\n` : '';
29
+ const request = parsed.variables ? `request: ${parsed.variables}\n` : '';
30
+ const response = parsed.response ? `response: ${parsed.response}` : 'response: string';
31
+ list.push(`POST /api/scripts/${parsed.id}\n${description}${request}${response}`);
32
+ } catch (err) {
33
+ console.error(err);
34
+ }
35
+ }
36
+ }
37
+ }
38
+
39
+ // Cache the list
40
+ scriptsList = list.join('\n\n');
41
+ }
42
+
43
+ return scriptsList;
44
+ }
45
+
46
+ export function clearCachedScripts(): void {
47
+ scriptsList = undefined;
48
+ }
49
+
50
+
51
+ export async function executeScript(args: ExecuteScriptArgs): Promise<AgentCompletion<ExecuteScriptResponse>> {
52
+ const { pagesFolder, scriptId, variables } = args;
53
+
54
+ // Load the script
55
+ const script = await loadFile(path.join(pagesFolder, `scripts`, `${scriptId}.json`));
56
+ if (!script) {
57
+ return { completed: false, error: new Error(`Script not found: ${scriptId}`) };
58
+ }
59
+
60
+ // Parse the script
61
+ let parsed: ParsedScript;
62
+ try {
63
+ parsed = JSON.parse(script) as ParsedScript;
64
+ } catch (err) {
65
+ return { completed: false, error: err as Error };
66
+ }
67
+
68
+ // Substitute variables
69
+ const commandText = composeArguments(parsed.command, variables);
70
+ console.log(`Executing: ${commandText}`);
71
+
72
+ // Split into an array while honoring double quotes
73
+ const argv = commandText.match(/"[^"]*"|\S+/g) || [];
74
+ if (argv.length === 0) {
75
+ return { completed: false, error: new Error('No command specified') };
76
+ }
77
+
78
+ // Execute the process
79
+ try {
80
+ const command = argv.shift() as string;
81
+ const value = await spawnProcess(command, argv);
82
+
83
+ return { completed: true, value };
84
+ } catch (err: unknown) {
85
+ return { completed: false, error: err as Error };
86
+ }
87
+ }
88
+
89
+ let scriptsList: string|undefined = undefined;
90
+
91
+ interface ParsedScript {
92
+ id: string;
93
+ type: 'command';
94
+ command: string;
95
+ description?: string;
96
+ variables?: string;
97
+ response?: string;
98
+ }
99
+
100
+ function spawnProcess(command: string, args: string[]): Promise<ExecuteScriptResponse> {
101
+ return new Promise((resolve, reject) => {
102
+ const child = spawn(command, args, { shell: true });
103
+
104
+ let output = '';
105
+ const errors: string[] = [];
106
+
107
+ child.stdout.on('data', (data) => {
108
+ output += data;
109
+ });
110
+
111
+ child.stderr.on('data', (data) => {
112
+ errors.push(data.toString());
113
+ });
114
+
115
+ child.on('close', (code) => {
116
+ resolve({ output, errors });
117
+ });
118
+
119
+ child.on('error', (err) => {
120
+ reject(err);
121
+ });
122
+ });
123
+ }
124
+
125
+ function composeArguments(template: string, variables: Record<string, any>): string {
126
+ return template.replace(/{{\s*([^}\s]+)\s*}}/g, (match, name) => {
127
+ // Convert the variable to a string and replace double quotes and line feeds with spaces
128
+ return variableToString(variables[name]);
129
+ });
130
+ }
@@ -0,0 +1,40 @@
1
+ import {anthropic, completePrompt, logCompletePrompt, openai} from 'agentm-core';
2
+ import { loadSettings } from '../settings';
3
+
4
+ export const availableModels = [
5
+ 'claude-3-5-sonnet-20240620',
6
+ 'gpt-4o-mini',
7
+ 'gpt-4o-2024-08-06',
8
+ 'o1-mini',
9
+ 'o1-preview'
10
+ ];
11
+
12
+ export async function createCompletePrompt(pagesFolder: string, model?: string): Promise<completePrompt> {
13
+ // Get configuration settings
14
+ const settings = await loadSettings(pagesFolder);
15
+ if (!settings.serviceApiKey) {
16
+ throw new Error('OpenAI API key not configured');
17
+ }
18
+
19
+ // Validate model
20
+ model = model || settings.model;
21
+ if (!model) {
22
+ throw new Error('Model not configured');
23
+ }
24
+
25
+ // Create completion functions
26
+ let modelInstance: completePrompt;
27
+ const apiKey = settings.serviceApiKey;
28
+ if (model.startsWith('claude-')) {
29
+ modelInstance = anthropic({apiKey, model});
30
+ } else {
31
+ modelInstance = openai({apiKey, model});
32
+ }
33
+
34
+ // Return new model instance
35
+ if (settings.logCompletions) {
36
+ return logCompletePrompt(modelInstance, true);
37
+ } else {
38
+ return modelInstance;
39
+ }
40
+ }
@@ -0,0 +1,101 @@
1
+ import OpenAI from "openai";
2
+ import { AgentCompletion, RequestError } from "agentm-core";
3
+ import { loadFile } from "../files";
4
+ import path from "path";
5
+
6
+ /**
7
+ * Arguments to configure an OpenAI chat model.
8
+ */
9
+ export interface OpenaiGenerateImageArgs {
10
+ /**
11
+ * OpenAI API key to use for completions.
12
+ */
13
+ apiKey: string;
14
+
15
+ /**
16
+ * Prompt to use for generating image.
17
+ */
18
+ prompt: string;
19
+
20
+ /**
21
+ * Requested size of the generated image.
22
+ */
23
+ shape: 'square' | 'landscape' | 'portrait';
24
+
25
+ /**
26
+ * Requested quality of the generated image.
27
+ */
28
+ quality: 'standard' | 'hd';
29
+
30
+ /**
31
+ * Style of the generated image.
32
+ */
33
+ style: 'vivid' | 'natural';
34
+ }
35
+
36
+ export interface GeneratedImage {
37
+ url: string;
38
+ }
39
+
40
+ export function generateDefaultImage(): Promise<AgentCompletion<GeneratedImage>> {
41
+ if (!_defaultImage) {
42
+ _defaultImage = new Promise<AgentCompletion<GeneratedImage>>(async resolve => {
43
+ const file = await loadFile(path.join(__dirname, '../../defaultImage.json'));
44
+ const value = JSON.parse(file) as GeneratedImage;
45
+ resolve({ completed: true, value });
46
+ });
47
+ }
48
+
49
+ return _defaultImage;
50
+ }
51
+
52
+ export async function generateImage(args: OpenaiGenerateImageArgs): Promise<AgentCompletion<GeneratedImage>> {
53
+ const { apiKey, prompt, shape, quality, style } = args;
54
+
55
+ // Create client
56
+ const client = new OpenAI({ apiKey });
57
+
58
+ // Identify image size
59
+ let size: "1024x1024" | "1792x1024" | "1024x1792";
60
+ switch (shape) {
61
+ case 'square':
62
+ default:
63
+ size = "1024x1024";
64
+ break;
65
+ case 'landscape':
66
+ size = "1792x1024";
67
+ break;
68
+ case 'portrait':
69
+ size = "1024x1792";
70
+ break;
71
+ }
72
+
73
+ try {
74
+ const response = await client.images.generate({
75
+ model: "dall-e-3",
76
+ response_format: "b64_json",
77
+ n: 1,
78
+ prompt,
79
+ size,
80
+ quality,
81
+ style
82
+ });
83
+
84
+ if (response.data.length > 0 && response.data[0].b64_json !== undefined) {
85
+ const url = `data:image/png;base64,${response.data[0].b64_json}`
86
+ return { completed: true, value: { url } };
87
+ } else {
88
+ return { completed: false, error: new Error('No image URL returned') };
89
+ }
90
+ } catch (err: unknown) {
91
+ let error: Error;
92
+ if (err instanceof OpenAI.APIError && err.status !== undefined) {
93
+ error = new RequestError(err.message, err.status, err.name);
94
+ } else {
95
+ error = err as Error;
96
+ }
97
+ return { completed: false, error };
98
+ }
99
+ }
100
+
101
+ let _defaultImage: Promise<AgentCompletion<GeneratedImage>> | undefined;
@@ -0,0 +1,7 @@
1
+ export * from './createCompletePrompt';
2
+ export * from './requiresSettings';
3
+ export * from './generateImage';
4
+ export * from './server';
5
+ export * from './transformPage';
6
+ export * from './useApiRoutes';
7
+ export * from './usePageRoutes';
@@ -0,0 +1,22 @@
1
+ import { hasConfiguredSettings, loadSettings, Settings } from "../settings";
2
+
3
+
4
+ export async function requiresSettings(res: any, folder: string, cb: (settings: Settings) => Promise<void>) {
5
+ try {
6
+ // Ensure settings configured
7
+ const isConfigured = await hasConfiguredSettings(folder);
8
+ if (!isConfigured) {
9
+ res.status(400).send('Settings not configured');
10
+ return;
11
+ }
12
+
13
+ // Load settings
14
+ const settings = await loadSettings(folder);
15
+
16
+ // Call the callback
17
+ await cb(settings);
18
+ } catch (err: unknown) {
19
+ console.error(err);
20
+ res.status(500).send((err as Error).message);
21
+ }
22
+ }
@@ -0,0 +1,26 @@
1
+ import express, {Application} from 'express';
2
+ import { usePageRoutes } from './usePageRoutes';
3
+ import { useApiRoutes } from './useApiRoutes';
4
+ import { SynthOSConfig } from '../init';
5
+ import { useDataRoutes } from './useDataRoutes';
6
+
7
+ export function server(config: SynthOSConfig): Application {
8
+ const app = express();
9
+
10
+ // Middleware to parse URL-encoded data (form data)
11
+ app.use(express.urlencoded({ extended: true }));
12
+
13
+ // Middleware to parse JSON data
14
+ app.use(express.json());
15
+
16
+ // Page handling routes
17
+ usePageRoutes(config, app);
18
+
19
+ // API routes
20
+ useApiRoutes(config, app);
21
+
22
+ // Data routes
23
+ useDataRoutes(config, app);
24
+
25
+ return app;
26
+ }
@@ -0,0 +1,135 @@
1
+ import { AgentArgs, AgentCompletion, generateObject, JsonSchema, SystemMessage, UserMessage } from "agentm-core";
2
+ import { listScripts } from "../scripts";
3
+
4
+ export interface TransformPageArgs extends AgentArgs {
5
+ pagesFolder: string;
6
+ pageState: string;
7
+ message: string;
8
+ maxTokens: number;
9
+ instructions?: string;
10
+ }
11
+
12
+ export async function transformPage(args: TransformPageArgs): Promise<AgentCompletion<string>> {
13
+ // Get list of registered scripts
14
+ const scripts = await listScripts(args.pagesFolder);
15
+ const serverScripts = scripts.length > 0 ? `<SERVER_SCRIPTS>\n${scripts}\n\n` : '';
16
+
17
+ // Define system message
18
+ const { pageState, message, maxTokens, completePrompt } = args;
19
+ const system: SystemMessage = {
20
+ role: 'system',
21
+ content: `<CURRENT_PAGE>\n${pageState}\n\n<SERVER_APIS>\n${serverAPIs}\n\n${serverScripts}<USER_MESSAGE>\n${message}`
22
+ };
23
+
24
+ // Create prompt
25
+ const instructions = args.instructions ? `\n\n<INSTRUCTIONS>\n${args.instructions}` : '';
26
+ const prompt: UserMessage = {
27
+ role: 'user',
28
+ content: `${goal}${instructions}`
29
+ };
30
+
31
+ // Complete prompt
32
+ const result = await completePrompt({ prompt, system, maxTokens });
33
+ if (result.completed) {
34
+ // Find html content
35
+ let start = result.value.indexOf(`<!DOCTYPE`);
36
+ start = start >= 0 ? start : result.value.indexOf('<html');
37
+ const end = result.value.lastIndexOf('</html>');
38
+ if (start >= 0 && end >= start) {
39
+ const value = result.value.substring(start, end + 7);
40
+ return { completed: true, value };
41
+ } else {
42
+ return { completed: false, error: new Error('Failed to find html content') };
43
+ }
44
+ } else {
45
+ return { completed: false, error: result.error };
46
+ }
47
+ }
48
+
49
+ export async function transformPageAsObject(args: TransformPageArgs): Promise<AgentCompletion<string>> {
50
+ // Get list of registered scripts
51
+ const scripts = await listScripts(args.pagesFolder);
52
+ const serverScripts = scripts.length > 0 ? `<SERVER_SCRIPTS>\n${scripts}\n\n` : '';
53
+
54
+ // Provide additional context
55
+ const { pageState, message, maxTokens, instructions, completePrompt, shouldContinue } = args;
56
+ const context = `<CURRENT_PAGE>\n${pageState}\n\n<SERVER_APIS>\n${serverAPIs}\n\n${serverScripts}<USER_MESSAGE>\n${message}`;
57
+
58
+ // Generate next page
59
+ const result = await generateObject<HtmlPage>({ goal, jsonSchema, maxTokens, context, instructions, completePrompt, shouldContinue });
60
+ if (result.completed) {
61
+ return { completed: true, value: result.value?.content! };
62
+ } else {
63
+ return { completed: false, error: result.error };
64
+ }
65
+ }
66
+
67
+ // Define output shape
68
+ interface HtmlPage {
69
+ content: string;
70
+ }
71
+
72
+ const jsonSchema: JsonSchema = {
73
+ name: 'HtmlPage',
74
+ schema: {
75
+ type: 'object',
76
+ properties: {
77
+ content: {
78
+ type: 'string',
79
+ description: 'html page content to return'
80
+ }
81
+ },
82
+ required: ['content'],
83
+ additionalProperties: false
84
+ },
85
+ strict: true
86
+ };
87
+
88
+ const goal =
89
+ `Generate a new web page that represents the next state of the chat based on the users message.
90
+ Append the the users message and a brief response from the AI to the chat panel.
91
+ Maintain the full conversation history in the chat panel unless asked to clear it.
92
+ Any details or visualizations should be rendered to the viewer panel.
93
+ The basic layout structure of the page needs to be maintained.
94
+ You're free to write any additional CSS or JavaScript to enhance the page.
95
+ Write an explication of your reasoning or any hidden thoughts to the thoughts div.
96
+ If the user asks to create something like an app, tool, game, or ui create it in the viewer panel.
97
+ If the user asks to draw something use canvas to draw it in the viewer panel.
98
+ Always return the full html content of the page.`;
99
+
100
+ const serverAPIs =
101
+ `GET /api/data/:table
102
+ description: Retrieve all rows from a table
103
+ response: Array of JSON rows [{ id: string, ... }]
104
+
105
+ GET /api/data/:table/:id
106
+ description: Retrieve a single row from a table
107
+ response: JSON row { id: string, ... }
108
+
109
+ POST /api/data/:table
110
+ description: Replaces or adds a single row to a table and returns the row
111
+ request: JSON row { id?: string, ... }
112
+ response: { id: string, ... }
113
+
114
+ DELETE /api/data/:table/:id
115
+ description: Delete a single row from a table
116
+ response: { success: true }
117
+
118
+ POST /api/generate/image
119
+ description: Generate an image based on a prompt
120
+ request: { prompt: string, shape: 'square' | 'portrait' | 'landscape', style: 'vivid' | 'natural' }
121
+ response: { url: string }
122
+
123
+ POST /api/generate/completion
124
+ description: Generates a text completion based on a prompt
125
+ request: { prompt: string, temperature?: number }
126
+ response: { answer: string, explanation: string }
127
+
128
+ GET /api/pages
129
+ description: Retrieve a list of all pages
130
+ response: Array of page names [string]
131
+
132
+ POST /api/scripts/:id
133
+ description: Execute a script with the passed in variables
134
+ request: { [key: string]: string }
135
+ response: string`;
@@ -0,0 +1,95 @@
1
+ import { listPages } from "../pages";
2
+ import {loadSettings, saveSettings } from "../settings";
3
+ import { Application } from 'express';
4
+ import { SynthOSConfig } from "../init";
5
+ import { availableModels, createCompletePrompt } from "./createCompletePrompt";
6
+ import { generateDefaultImage, generateImage } from "./generateImage";
7
+ import { chainOfThought } from "agentm-core";
8
+ import { requiresSettings } from "./requiresSettings";
9
+ import { executeScript } from "../scripts";
10
+
11
+ export function useApiRoutes(config: SynthOSConfig, app: Application): void {
12
+ // List pages
13
+ app.get('/api/pages', async (req, res) => {
14
+ const pages = await listPages(config.pagesFolder, config.requiredPagesFolder);
15
+ res.json(pages);
16
+ });
17
+
18
+ // Define a route to return settings
19
+ app.get('/api/settings', async (req, res) => {
20
+ const settings = await loadSettings(config.pagesFolder);
21
+ res.json({...settings, availableModels});
22
+ });
23
+
24
+ // Define a route to save settings
25
+ app.post('/api/settings', async (req, res) => {
26
+ try {
27
+ // Covert non-string values
28
+ const settings = req.body as Record<string, any>;
29
+ if (typeof settings.maxTokens === 'string') {
30
+ settings.maxTokens = parseInt(settings.maxTokens);
31
+ }
32
+ if (typeof settings.logCompletions === 'string') {
33
+ settings.logCompletions = settings.logCompletions === 'true';
34
+ }
35
+
36
+ // Save settings
37
+ await saveSettings(config.pagesFolder, settings);
38
+ res.redirect('/home');
39
+ } catch (err: unknown) {
40
+ console.error(err);
41
+ res.status(500).send((err as Error).message);
42
+ }
43
+ });
44
+
45
+ // Define a route to generate an image
46
+ app.post('/api/generate/image', async (req, res) => {
47
+ await requiresSettings(res, config.pagesFolder, async (settings) => {
48
+ const { prompt, shape, style } = req.body;
49
+ const { serviceApiKey, imageQuality, model } = settings;
50
+ const response = model.startsWith('gpt-') ?
51
+ await generateImage({ apiKey: serviceApiKey, prompt, shape, quality: imageQuality, style }) :
52
+ await generateDefaultImage();
53
+ if (response.completed) {
54
+ res.json(response.value);
55
+ } else {
56
+ res.status(500).send(response.error?.message);
57
+ }
58
+ });
59
+ });
60
+
61
+ // Define a route to generate a completion using chain-of-thought
62
+ app.post('/api/generate/completion', async (req, res) => {
63
+ await requiresSettings(res, config.pagesFolder, async (settings) => {
64
+ const { prompt, temperature } = req.body;
65
+ const { maxTokens } = settings;
66
+ const completePrompt = await createCompletePrompt(config.pagesFolder, req.body.model);
67
+ const response = await chainOfThought({ question: prompt, temperature, maxTokens, completePrompt });
68
+ if (response.completed) {
69
+ res.json(response.value ?? {});
70
+ } else {
71
+ console.error(response.error);
72
+ res.status(500).send(response.error?.message);
73
+ }
74
+ });
75
+ });
76
+
77
+ // Define a route for running configured scripts
78
+ app.post('/api/scripts/:id', async (req, res) => {
79
+ await requiresSettings(res, config.pagesFolder, async (settings) => {
80
+ const { id } = req.params;
81
+ const pagesFolder = config.pagesFolder;
82
+ const scriptId = id;
83
+ const response = await executeScript({ pagesFolder, scriptId, variables: req.body });
84
+ if (response.completed) {
85
+ // Return the response as text
86
+ const value = (response.value?.output ?? (response.value?.errors ?? []).join('\n')).trim();
87
+ res.set('Content-Type', 'text/plain');
88
+ res.send(value);
89
+ } else {
90
+ console.error(response.error);
91
+ res.status(500).send(response.error);
92
+ }
93
+ });
94
+ });
95
+ }