diff --git a/cli/README.md b/cli/README.md deleted file mode 100644 index f42f02f5a..000000000 --- a/cli/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# AIConfig CLI - -## Development - -In cli directory, development: - -``` -yarn && cd aiconfig-editor && yarn && cd .. -yarn dev devserver -``` - -## Building - -In cli directory: - -``` -yarn build -``` - -Using `./dist/index.js` with `yarn start`: - -``` -yarn start server -``` - -Can also specify different ports with `-p`: - -``` -yarn start server -p 3001 -``` diff --git a/cli/aiconfig-editor/.eslintrc.json b/cli/aiconfig-editor/.eslintrc.json deleted file mode 100644 index bffb357a7..000000000 --- a/cli/aiconfig-editor/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "next/core-web-vitals" -} diff --git a/cli/aiconfig-editor/.gitignore b/cli/aiconfig-editor/.gitignore deleted file mode 100644 index fd3dbb571..000000000 --- a/cli/aiconfig-editor/.gitignore +++ /dev/null @@ -1,36 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js -.yarn/install-state.gz - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# local env files -.env*.local - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts diff --git a/cli/aiconfig-editor/@types/ufetch.d.ts b/cli/aiconfig-editor/@types/ufetch.d.ts deleted file mode 100644 index 926c50e31..000000000 --- a/cli/aiconfig-editor/@types/ufetch.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -declare module "ufetch" { - export namespace ufetch { - function setCookie(key: string, value: string, expiry: number): void; - function getCookie(key: string): string; - - function post(path: string, data: any, options?: any); - function get(path: string, options?: any); - function put(path: string, data: any, options?: any); - function _delete(path: string, data: any, options?: any); - - export { _delete as delete, setCookie, getCookie, post, get, put }; - } -} diff --git a/cli/aiconfig-editor/README.md b/cli/aiconfig-editor/README.md deleted file mode 100644 index 1c6f30f85..000000000 --- a/cli/aiconfig-editor/README.md +++ /dev/null @@ -1,34 +0,0 @@ -This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). - -## Getting Started - -First, run the development server: - -```bash -yarn dev -``` - -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. - -You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file. - -[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. - -The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. - -This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. - -## Learn More - -To learn more about Next.js, take a look at the following resources: - -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! - -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/cli/aiconfig-editor/next.config.js b/cli/aiconfig-editor/next.config.js deleted file mode 100644 index a843cbee0..000000000 --- a/cli/aiconfig-editor/next.config.js +++ /dev/null @@ -1,6 +0,0 @@ -/** @type {import('next').NextConfig} */ -const nextConfig = { - reactStrictMode: true, -} - -module.exports = nextConfig diff --git a/cli/aiconfig-editor/package.json b/cli/aiconfig-editor/package.json deleted file mode 100644 index 94430b50e..000000000 --- a/cli/aiconfig-editor/package.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "aiconfig-editor", - "version": "0.1.0", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "next lint" - }, - "dependencies": { - "@emotion/react": "^11.11.1", - "@mantine/carousel": "^6.0.7", - "@mantine/core": "^6.0.7", - "@mantine/dates": "^6.0.16", - "@mantine/dropzone": "^6.0.7", - "@mantine/form": "^6.0.7", - "@mantine/hooks": "^6.0.7", - "@mantine/modals": "^6.0.7", - "@mantine/next": "^6.0.7", - "@mantine/notifications": "^6.0.7", - "@mantine/nprogress": "^6.0.7", - "@mantine/prism": "^6.0.7", - "@mantine/spotlight": "^6.0.7", - "@mantine/tiptap": "^6.0.7", - "@tabler/icons-react": "^2.44.0", - "aiconfig": "^1.1.0", - "lodash": "^4.17.21", - "next": "14.0.2", - "node-fetch": "^3.3.2", - "react": "^18", - "react-dom": "^18", - "react-markdown": "^8.0.6", - "remark-gfm": "^4.0.0", - "ufetch": "^1.6.0" - }, - "devDependencies": { - "@types/lodash": "^4.14.202", - "@types/node": "^20", - "@types/react": "^18", - "@types/react-dom": "^18", - "eslint": "^8", - "eslint-config-next": "14.0.2", - "typescript": "^5" - } -} diff --git a/cli/aiconfig-editor/pages/_app.tsx b/cli/aiconfig-editor/pages/_app.tsx deleted file mode 100644 index fc5a17fb0..000000000 --- a/cli/aiconfig-editor/pages/_app.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import type { AppProps } from "next/app"; - -import { MantineProvider } from "@mantine/core"; -import { Notifications } from "@mantine/notifications"; -import { useColorScheme } from "@mantine/hooks"; - -export default function App({ Component, pageProps }: AppProps) { - const preferredColorScheme = useColorScheme(); - - return ( - - - - - ); -} diff --git a/cli/aiconfig-editor/pages/_document.tsx b/cli/aiconfig-editor/pages/_document.tsx deleted file mode 100644 index 54e8bf3e2..000000000 --- a/cli/aiconfig-editor/pages/_document.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { Html, Head, Main, NextScript } from 'next/document' - -export default function Document() { - return ( - - - -
- - - - ) -} diff --git a/cli/aiconfig-editor/pages/api/aiconfig/load.ts b/cli/aiconfig-editor/pages/api/aiconfig/load.ts deleted file mode 100644 index 2eb5e39c6..000000000 --- a/cli/aiconfig-editor/pages/api/aiconfig/load.ts +++ /dev/null @@ -1,62 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from "next"; -import { AIConfigRuntime } from "aiconfig"; -import { ErrorResponse } from "@/src/shared/serverTypes"; -import { - ClientAIConfig, - ClientExecuteResult, - ClientPromptOutput, -} from "@/src/shared/types"; - -type Data = { - aiconfig: ClientAIConfig; -}; - -type RequestBody = { - path: string; -}; - -export default function handler( - req: NextApiRequest, - res: NextApiResponse -) { - if (req.method !== "POST") { - return res.status(500).json({ error: "Method not allowed" }); - } - - const body: RequestBody = req.body; - - if (!body.path) { - return res.status(500).json({ error: "No path provided" }); - } - - // TODO: load should probably be async? - const aiconfig = AIConfigRuntime.load(body.path); - - // Refine outputs for client-side rendering. We only care about displaying (and deleting) - // outputs directly from the editor - const clientAIConfig: ClientAIConfig = { - ...aiconfig, - prompts: aiconfig.prompts.map((prompt) => ({ - ...prompt, - outputs: prompt.outputs?.map((output) => { - if (output.output_type === "execute_result") { - const text = aiconfig.getOutputText(prompt, output); - if (text) { - // TODO: Once AIConfig output types are updated to be more structured, revisit and - // ideally remove Client-specific types - return { - ...output, - renderData: { - type: "text", - text, - }, - } as ClientExecuteResult; - } - } - return output as ClientPromptOutput; - }), - })), - }; - - res.status(200).json({ aiconfig: clientAIConfig }); -} diff --git a/cli/aiconfig-editor/pages/api/aiconfig/save.ts b/cli/aiconfig-editor/pages/api/aiconfig/save.ts deleted file mode 100644 index 30d05ddad..000000000 --- a/cli/aiconfig-editor/pages/api/aiconfig/save.ts +++ /dev/null @@ -1,56 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from "next"; -import { ErrorResponse } from "@/src/shared/serverTypes"; -import { AIConfig, AIConfigRuntime, Output } from "aiconfig"; -import { ClientAIConfig } from "@/src/shared/types"; - -type Data = { - status: string; -}; - -type RequestBody = { - path: string; - aiconfig: ClientAIConfig; -}; - -export default async function handler( - req: NextApiRequest, - res: NextApiResponse -) { - if (req.method !== "POST") { - return res.status(500).json({ error: "Method not allowed" }); - } - - const body: RequestBody = req.body; - - if (!body.path) { - return res.status(500).json({ error: "No path provided" }); - } - - if (!body.aiconfig) { - return res.status(500).json({ error: "No aiconfig data provided" }); - } - - // TODO: Once ouputs are properly structured, remove this and use body.aiconfig directly - const clientAIConfig = body.aiconfig; - const config = { - ...clientAIConfig, - prompts: clientAIConfig.prompts.map((prompt) => ({ - ...prompt, - outputs: prompt.outputs?.map((output) => { - if (output.output_type === "execute_result") { - const outputWithoutRenderData = { ...output, renderData: undefined }; - delete outputWithoutRenderData.renderData; - return outputWithoutRenderData as Output; - } else { - return output as Output; - } - }), - })), - }; - - // Construct the config and ensure proper serialization for saving - const serializedConfig = await AIConfigRuntime.loadJSON(config); - serializedConfig.save(body.path, { serializeOutputs: true }); - - res.status(200).json({ status: "ok" }); -} diff --git a/cli/aiconfig-editor/pages/api/files.ts b/cli/aiconfig-editor/pages/api/files.ts deleted file mode 100644 index e40571555..000000000 --- a/cli/aiconfig-editor/pages/api/files.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from "next"; - -import { promises as fs } from "fs"; -import path from "path"; -import { EditorFile } from "@/src/shared/types"; - -type Data = { - files: EditorFile[]; -}; - -type Error = { - error: string; -}; - -type RequestBody = { - path?: string; -}; - -export default async function handler( - req: NextApiRequest, - res: NextApiResponse -) { - if (req.method !== "POST") { - return res.status(500).json({ error: "Method not allowed" }); - } - - const body: RequestBody = req.body; - - const relativePath = body.path ? body.path : "."; - - const directoryToRead = path.isAbsolute(relativePath) - ? relativePath - : path.join(process.cwd(), relativePath); - - const files = await fs.readdir(path.join(directoryToRead), { - withFileTypes: true, - }); - - const filesResponse = files.map((file) => { - const extension = path.extname(file.name); - - return { - name: file.name, - extension, - path: file.path, - isDirectory: file.isDirectory(), - }; - }); - - return res.status(200).json({ files: filesResponse }); -} diff --git a/cli/aiconfig-editor/pages/editor.tsx b/cli/aiconfig-editor/pages/editor.tsx deleted file mode 100644 index 4a4113fb6..000000000 --- a/cli/aiconfig-editor/pages/editor.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import EditorContainer from "@/src/components/EditorContainer"; -import { ClientAIConfig } from "@/src/shared/types"; -import { Flex, Loader } from "@mantine/core"; -import { AIConfig } from "aiconfig"; -import { useRouter } from "next/router"; -import { useCallback, useEffect, useState } from "react"; -import { ufetch } from "ufetch"; - -export default function Editor() { - // Use router to get the path, load the file using aiconfig.load, make it editable & use save to save it regularly - // TODO: Settings, other things to edit, allowing plugins in editor (eg for custom model parsers in python or JS) - const router = useRouter(); - const [aiconfig, setAiConfig] = useState(); - - const loadConfig = useCallback(async () => { - if (!router.query.path) { - return; - } - - const res = await ufetch.post(`/api/aiconfig/load`, { - path: router.query.path, - }); - - setAiConfig(res.aiconfig); - }, [router]); - - useEffect(() => { - loadConfig(); - }, [loadConfig]); - - const onBackNavigation = useCallback(() => { - if (!router.query.path) { - return; - } else { - router.back(); - } - }, [router]); - - const onSave = useCallback( - async (aiconfig: AIConfig) => { - const res = await ufetch.post(`/api/aiconfig/save`, { - path: router.query.path, - aiconfig, - }); - return res; - }, - [router] - ); - - return ( -
- {!aiconfig ? ( - - - - ) : ( - - )} -
- ); -} diff --git a/cli/aiconfig-editor/pages/index.tsx b/cli/aiconfig-editor/pages/index.tsx deleted file mode 100644 index 89b173050..000000000 --- a/cli/aiconfig-editor/pages/index.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import { EditorFile } from "@/src/shared/types"; -import { Button, Container, Group, Table, Text } from "@mantine/core"; -import Head from "next/head"; -import Link from "next/link"; -import { useRouter } from "next/router"; -import { useCallback, useEffect, useState } from "react"; -import { ufetch } from "ufetch"; - -export default function Home() { - // Get list of files in the currect directory; this should also use a path from the router to become a nested file tree that can be navigated - const router = useRouter(); - - const [files, setFiles] = useState([]); - - const getFiles = useCallback(async () => { - const res = await ufetch.post("/api/files", { path: router.query.path }); - - const newFiles = res.files.map((file: EditorFile) => { - const disabled = !file.isDirectory && file.extension !== ".json"; - - return { - ...file, - disabled, - }; - }); - - setFiles(newFiles); - }, [router.query.path]); - - useEffect(() => { - // Get files from current directory (will be whatever is passed as root directory when undefined, but needs to be handled server side - getFiles(); - }, [getFiles]); - - const navigate = useCallback( - (file: EditorFile) => { - // If directory, then change path & also update the url route to add to history - // If random file, should not be able to select - // If aiconfig.json file, then open editor - can't actually detect yet, so will just try to parse - // Use load_json - console.log("navigate", file); - console.log(file.isDirectory); - - const newPath = `${file.path}/${file.name}`; - if (file.isDirectory) { - router.push(`?path=${encodeURIComponent(newPath)}`); - } else { - router.push(`/editor?path=${encodeURIComponent(newPath)}`); - // Uncomment to open editor in new tab - // window.open(`/editor?path=${encodeURIComponent(file.path)}`, "_blank"); - } - }, - [router] - ); - - const back = useCallback(() => { - if (!router.query.path) { - return; - } else { - router.back(); - } - }, [router]); - - return ( - <> - - AIConfig Editor - - - - -
- - - -
- - {router.query.path || "/"} - -
- -
- - - - - - - - - {files.map((file, i) => ( - - - - - ))} - -
FileActions
{file.name} - -
-
-
- - ); -} diff --git a/cli/aiconfig-editor/public/favicon.ico b/cli/aiconfig-editor/public/favicon.ico deleted file mode 100644 index 718d6fea4..000000000 Binary files a/cli/aiconfig-editor/public/favicon.ico and /dev/null differ diff --git a/cli/aiconfig-editor/src/components/EditorContainer.tsx b/cli/aiconfig-editor/src/components/EditorContainer.tsx deleted file mode 100644 index 2a3180ebd..000000000 --- a/cli/aiconfig-editor/src/components/EditorContainer.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import PromptContainer from "@/src/components/prompt/PromptContainer"; -import { Container, Text, Group, Button, createStyles } from "@mantine/core"; -import { showNotification } from "@mantine/notifications"; -import { AIConfig, PromptInput } from "aiconfig"; -import router from "next/router"; -import { useCallback, useReducer, useState } from "react"; -import aiconfigReducer from "@/src/components/aiconfigReducer"; -import { ClientAIConfig, clientConfigToAIConfig } from "@/src/shared/types"; - -type Props = { - aiconfig: ClientAIConfig; - onBackNavigation: () => void; - onSave: (aiconfig: AIConfig) => Promise; -}; - -const useStyles = createStyles((theme) => ({ - promptsContainer: { - [theme.fn.smallerThan("sm")]: { - padding: "0 0 200px 0", - }, - paddingBottom: 400, - }, -})); - -export default function EditorContainer({ - aiconfig: initialAIConfig, - onBackNavigation, - onSave, -}: Props) { - const [isSaving, setIsSaving] = useState(false); - const [aiconfigState, dispatch] = useReducer( - aiconfigReducer, - initialAIConfig - ); - - const save = useCallback(async () => { - setIsSaving(true); - try { - await onSave(clientConfigToAIConfig(aiconfigState)); - } catch (err: any) { - showNotification({ - title: "Error saving", - message: err.message, - color: "red", - }); - } finally { - setIsSaving(false); - } - }, [aiconfigState, onSave]); - - const onChangePromptInput = useCallback( - async (promptIndex: number, newPromptInput: PromptInput) => { - dispatch({ - type: "UPDATE_PROMPT_INPUT", - index: promptIndex, - input: newPromptInput, - }); - // TODO: Call server-side endpoint to update prompt input - }, - [dispatch] - ); - - const onUpdatePromptModelSettings = useCallback( - async (promptIndex: number, newModelSettings: any) => { - dispatch({ - type: "UPDATE_PROMPT_MODEL_SETTINGS", - index: promptIndex, - modelSettings: newModelSettings, - }); - // TODO: Call server-side endpoint to update model settings - }, - [dispatch] - ); - - const { classes } = useStyles(); - - // TODO: Implement editor context for callbacks, readonly state, etc. - - return ( - <> - - - - - {router.query?.path || "No path specified"} - - - - - - {aiconfigState.prompts.map((prompt: any, i: number) => { - return ( - - ); - })} - - - ); -} diff --git a/cli/aiconfig-editor/src/components/SettingsPropertyRenderer.tsx b/cli/aiconfig-editor/src/components/SettingsPropertyRenderer.tsx deleted file mode 100644 index f1c356acb..000000000 --- a/cli/aiconfig-editor/src/components/SettingsPropertyRenderer.tsx +++ /dev/null @@ -1,418 +0,0 @@ -import { - Text, - Group, - Stack, - Autocomplete, - Tooltip, - NumberInput, - TextInput, - Slider, - Checkbox, - ActionIcon, - Textarea, - AutocompleteItem, - Select, -} from "@mantine/core"; -import { useState, useCallback, useRef, useEffect } from "react"; -import { uniqueId } from "lodash"; -import { IconHelp, IconPlus, IconTrash } from "@tabler/icons-react"; -import UnionPropertyControl, { - UnionProperty, -} from "@/src/components/property_controls/UnionPropertyControl"; - -type StateSetFromPrevFn = (prev: any) => void; -export type SetStateFn = (val: StateSetFromPrevFn | any) => void; - -export type PropertyRendererProps = { - propertyName: string; - property: { [key: string]: any }; - isRequired?: boolean; - initialValue?: any; - setValue: SetStateFn; -}; - -export function PropertyLabel(props: { - propertyName: string; - propertyDescription: string; -}) { - const { propertyName, propertyDescription } = props; - return propertyDescription != null && propertyDescription.trim() !== "" ? ( - - {propertyName} - - - - - - - - ) : ( - {propertyName} - ); -} - -export default function SettingsPropertyRenderer({ - propertyName, - property, - isRequired = false, - initialValue = null, - setValue, -}: PropertyRendererProps) { - const propertyType = property.type; - const defaultValue = property.default; - const propertyDescription = property.description; - const [propertyValue, setPropertyValue] = useState( - initialValue ?? defaultValue - ); - - let propertyControl; - - const setAndPropagateValue = useCallback( - (newValue: ((prev: any) => void) | any) => { - const valueToSet = - typeof newValue === "function" ? newValue(propertyValue) : newValue; - - if (propertyName != null && propertyName.trim() !== "") { - setValue((prevValue: any) => ({ - ...(prevValue && typeof prevValue === "object" ? prevValue : {}), - [propertyName]: valueToSet, - })); - } else { - setValue(valueToSet); - } - - setPropertyValue(valueToSet); - }, - [propertyName, propertyValue, setValue] - ); - - // Used in the case the property is an array - // TODO: Should initialize with values from settings if available - const [itemControls, setItemControls] = useState([]); - const itemValues = useRef(new Map()); - - const removeItemFromList = useCallback( - async (key: string) => { - setItemControls((prevItemControls) => - prevItemControls.filter((item) => item.key !== key) - ); - - itemValues.current.delete(key); - setAndPropagateValue(Array.from(itemValues.current.values())); - }, - [setAndPropagateValue] - ); - - const addItemToList = useCallback(async () => { - const key = uniqueId(); - setItemControls((prevItemControls) => [ - ...prevItemControls, - - { - itemValues.current.set(key, newItem); - setAndPropagateValue(Array.from(itemValues.current.values())); - }} - /> - removeItemFromList(key)}> - - - , - ]); - }, [property.items, removeItemFromList, setAndPropagateValue]); - - switch (propertyType) { - case "string": { - if (property.enum != null) { - propertyControl = ( - - } - filter={(value: string, item: AutocompleteItem) => { - const label: string = item.value.toLocaleLowerCase(); - const val = value.toLocaleLowerCase().trim(); - - // If selected value matches enum exactly (selected case), show all options - if ( - property.enum && - property.enum.some((v: string) => v === val) - ) { - return true; - } - - // Include item if typed value is a substring - return label.includes(val); - }} - required={isRequired} - placeholder={propertyValue ?? "select"} - data={property.enum} - value={propertyValue ?? ""} - onChange={setAndPropagateValue} - /> - ); - } else { - propertyControl = ( - - } - placeholder={propertyValue} - required={isRequired} - withAsterisk={isRequired} - radius="md" - value={propertyValue ?? ""} - onChange={(event) => - setAndPropagateValue(event.currentTarget.value) - } - /> - ); - } - break; - } - case "text": { - propertyControl = ( -