-
Notifications
You must be signed in to change notification settings - Fork 12
Feat: Use thread unique workspace, add knowledge ingest #201
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,3 +41,5 @@ next-env.d.ts | |
| # app output directories | ||
| /gptscripts | ||
| /threads | ||
|
|
||
| bin/ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| 'use server'; | ||
| import path from 'path'; | ||
| import { exec } from 'child_process'; | ||
| import { promisify } from 'util'; | ||
|
|
||
| const execPromise = promisify(exec); | ||
|
|
||
| export async function ingest( | ||
| workspace: string, | ||
| token: string | undefined, | ||
| datasetID?: string | null | ||
| ): Promise<void> { | ||
| if (!datasetID) { | ||
| throw new Error('Dataset ID is required'); | ||
| } | ||
| const dir = path.join(workspace, 'knowledge'); | ||
| const knowledgeBinaryPath = process.env.KNOWLEDGE_BIN; | ||
| await execPromise( | ||
| `${knowledgeBinaryPath} ingest --dataset ${datasetID} ${dir.replace(/ /g, '\\ ')}`, | ||
| { env: { ...process.env, GPTSCRIPT_GATEWAY_API_KEY: token } } | ||
| ); | ||
| return; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| 'use server'; | ||
|
|
||
| import { GATEWAY_URL, THREADS_DIR, WORKSPACE_DIR } from '@/config/env'; | ||
| import { THREADS_DIR, WORKSPACE_DIR } from '@/config/env'; | ||
| import { gpt } from '@/config/env'; | ||
| import fs from 'fs/promises'; | ||
| import path from 'path'; | ||
|
|
@@ -24,22 +24,14 @@ export type ThreadMeta = { | |
| workspace: string; | ||
| }; | ||
|
|
||
| export async function init() { | ||
| const threadsDir = THREADS_DIR(); | ||
| try { | ||
| await fs.access(threadsDir); | ||
| } catch (error) { | ||
| await fs.mkdir(threadsDir, { recursive: true }); | ||
| } | ||
| } | ||
|
|
||
| export async function getThreads() { | ||
| const threads: Thread[] = []; | ||
| const threadsDir = THREADS_DIR(); | ||
|
|
||
| let threadDirs: void | string[] = []; | ||
| try { | ||
| threadDirs = await fs.readdir(threadsDir); | ||
| // eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
| } catch (e) { | ||
| return []; | ||
| } | ||
|
|
@@ -49,18 +41,19 @@ export async function getThreads() { | |
| for (const threadDir of threadDirs) { | ||
| const threadPath = path.join(threadsDir, threadDir); | ||
| const files = await fs.readdir(threadPath); | ||
| const stateFile = path.join(threadPath, STATE_FILE); | ||
|
|
||
| const thread: Thread = {} as Thread; | ||
| if (files.includes(STATE_FILE)) { | ||
| const state = await fs.readFile( | ||
| path.join(threadPath, STATE_FILE), | ||
| 'utf-8' | ||
| ); | ||
| const state = await fs.readFile(stateFile, 'utf-8'); | ||
| thread.state = state; | ||
| } | ||
| if (files.includes(META_FILE)) { | ||
| const meta = await fs.readFile(path.join(threadPath, META_FILE), 'utf-8'); | ||
| const stateStats = await fs.stat(stateFile); | ||
| const lastModified = stateStats.mtime; | ||
| thread.meta = JSON.parse(meta) as ThreadMeta; | ||
| thread.meta.updated = lastModified; | ||
| } else { | ||
| continue; | ||
| } | ||
|
|
@@ -96,23 +89,27 @@ export async function generateThreadName( | |
|
|
||
| export async function createThread( | ||
| script: string, | ||
| firstMessage?: string, | ||
| scriptId?: string, | ||
| workspace?: string | ||
| threadName?: string, | ||
| scriptId?: string | ||
| ): Promise<Thread> { | ||
| const threadsDir = THREADS_DIR(); | ||
|
|
||
| // will probably want something else for this | ||
| const id = Math.random().toString(36).substring(7); | ||
| const threadPath = path.join(threadsDir, id); | ||
| await fs.mkdir(threadPath, { recursive: true }); | ||
| const workspace = path.join(threadPath, 'workspace'); | ||
| await fs.mkdir(workspace, { recursive: true }); | ||
|
|
||
| if (!threadName) { | ||
| threadName = await newThreadName(); | ||
| } | ||
| const threadMeta = { | ||
| name: await newThreadName(), | ||
| name: threadName, | ||
| description: '', | ||
| created: new Date(), | ||
| updated: new Date(), | ||
| workspace: workspace ?? WORKSPACE_DIR(), | ||
| workspace: workspace, | ||
| id, | ||
| scriptId: scriptId || '', | ||
| script, | ||
|
|
@@ -125,11 +122,6 @@ export async function createThread( | |
| ); | ||
| await fs.writeFile(path.join(threadPath, STATE_FILE), ''); | ||
|
|
||
| if (firstMessage) { | ||
| const generatedThreadName = await generateThreadName(firstMessage); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As I understand it, it was a requirement to have the LLM name the thread based on the first message. Has that requirement changed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I changed it because the thread will always be created at first and it is kind of weird that the name will be changed later after user type the message. But yeah I will confirm with craig. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will change this, thanks for pointting out |
||
| await renameThread(id, generatedThreadName); | ||
| } | ||
|
|
||
| return { | ||
| state: threadState, | ||
| meta: threadMeta, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is where we run knowledge ingest command on server side to ingest file on fly.