@promptbook/cli 0.103.0-46 → 0.103.0-48
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.
- package/apps/agents-server/TODO.txt +2 -2
- package/apps/agents-server/config.ts.todo +5 -279
- package/apps/agents-server/next.config.ts +3 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/book/route.ts +1 -1
- package/apps/agents-server/src/app/agents/[agentName]/api/book/test.http +2 -2
- package/apps/agents-server/src/app/agents/[agentName]/api/chat/route.ts +13 -5
- package/apps/agents-server/src/app/agents/[agentName]/api/modelRequirements/TODO.txt +1 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/modelRequirements/route.ts +53 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/modelRequirements/systemMessage/route.ts +45 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/profile/route.ts +54 -0
- package/apps/agents-server/src/app/agents/[agentName]/book/BookEditorWrapper.tsx +84 -18
- package/apps/agents-server/src/app/agents/[agentName]/book/page.tsx +9 -5
- package/apps/agents-server/src/app/agents/[agentName]/book+chat/{SelfLearningBook.tsx → AgentBookAndChatComponent.tsx} +5 -48
- package/apps/agents-server/src/app/agents/[agentName]/book+chat/page.tsx +7 -4
- package/apps/agents-server/src/app/agents/[agentName]/chat/AgentChatWrapper.tsx +38 -0
- package/apps/agents-server/src/app/agents/[agentName]/chat/page.tsx +23 -0
- package/apps/agents-server/src/app/agents/[agentName]/page.tsx +8 -6
- package/apps/agents-server/src/app/agents/page.tsx +11 -0
- package/apps/agents-server/src/app/api/chat/route.ts +1 -1
- package/apps/agents-server/src/app/api/chat-streaming/route.ts +5 -1
- package/apps/agents-server/src/app/api/upload/route.ts +71 -0
- package/apps/agents-server/src/{supabase/getSupabase.ts → database/$provideSupabase.ts} +11 -7
- package/apps/agents-server/src/{supabase/getSupabaseForBrowser.ts → database/$provideSupabaseForBrowser.ts} +9 -5
- package/apps/agents-server/src/{supabase/getSupabaseForServer.ts → database/$provideSupabaseForServer.ts} +4 -4
- package/apps/agents-server/src/{supabase/getSupabaseForWorker.ts → database/$provideSupabaseForWorker.ts} +5 -4
- package/apps/agents-server/src/database/schema.sql +131 -0
- package/apps/agents-server/src/database/schema.ts +217 -0
- package/apps/agents-server/src/tools/$provideAgentCollectionForServer.ts +3 -2
- package/apps/agents-server/src/tools/$provideCdnForServer.ts +28 -0
- package/apps/agents-server/src/tools/$provideOpenAiAssistantExecutionToolsForServer.ts +1 -1
- package/apps/agents-server/src/utils/cdn/classes/DigitalOceanSpaces.ts +119 -0
- package/apps/agents-server/src/utils/cdn/interfaces/IFilesStorage.ts +32 -0
- package/apps/agents-server/src/utils/cdn/interfaces/IStorage.ts +14 -0
- package/apps/agents-server/src/utils/cdn/utils/getUserFileCdnKey.ts +28 -0
- package/apps/agents-server/src/utils/cdn/utils/nameToSubfolderPath.ts +9 -0
- package/apps/agents-server/src/utils/cdn/utils/nextRequestToNodeRequest.ts +27 -0
- package/apps/agents-server/src/utils/validators/validateMimeType.ts +24 -0
- package/apps/agents-server/tsconfig.json +1 -1
- package/esm/index.es.js +319 -181
- package/esm/index.es.js.map +1 -1
- package/esm/typings/servers.d.ts +1 -7
- package/esm/typings/src/_packages/components.index.d.ts +4 -0
- package/esm/typings/src/_packages/core.index.d.ts +22 -14
- package/esm/typings/src/_packages/types.index.d.ts +14 -6
- package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +7 -3
- package/esm/typings/src/book-2.0/agent-source/AgentModelRequirements.d.ts +6 -1
- package/esm/typings/src/book-2.0/agent-source/AgentSourceParseResult.d.ts +3 -2
- package/esm/typings/src/book-2.0/agent-source/computeAgentHash.d.ts +8 -0
- package/esm/typings/src/book-2.0/agent-source/computeAgentHash.test.d.ts +1 -0
- package/esm/typings/src/book-2.0/agent-source/createCommitmentRegex.d.ts +1 -1
- package/esm/typings/src/book-2.0/agent-source/createDefaultAgentName.d.ts +8 -0
- package/esm/typings/src/book-2.0/agent-source/normalizeAgentName.d.ts +9 -0
- package/esm/typings/src/book-2.0/agent-source/normalizeAgentName.test.d.ts +1 -0
- package/esm/typings/src/book-2.0/agent-source/parseAgentSourceWithCommitments.d.ts +1 -1
- package/esm/typings/src/book-components/Chat/AgentChat/AgentChat.d.ts +14 -0
- package/esm/typings/src/book-components/Chat/AgentChat/AgentChat.test.d.ts +1 -0
- package/esm/typings/src/book-components/Chat/AgentChat/AgentChatProps.d.ts +13 -0
- package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +1 -60
- package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentsDatabaseSchema.d.ts +57 -32
- package/esm/typings/src/{book-2.0/commitments → commitments}/ACTION/ACTION.d.ts +1 -1
- package/esm/typings/src/{book-2.0/commitments → commitments}/DELETE/DELETE.d.ts +1 -1
- package/esm/typings/src/{book-2.0/commitments → commitments}/FORMAT/FORMAT.d.ts +1 -1
- package/esm/typings/src/{book-2.0/commitments → commitments}/GOAL/GOAL.d.ts +1 -1
- package/esm/typings/src/{book-2.0/commitments → commitments}/KNOWLEDGE/KNOWLEDGE.d.ts +1 -5
- package/esm/typings/src/{book-2.0/commitments → commitments}/MEMORY/MEMORY.d.ts +1 -1
- package/esm/typings/src/{book-2.0/commitments → commitments}/MESSAGE/MESSAGE.d.ts +1 -1
- package/esm/typings/src/{book-2.0/commitments → commitments}/META/META.d.ts +1 -1
- package/esm/typings/src/{book-2.0/commitments → commitments}/META_IMAGE/META_IMAGE.d.ts +1 -1
- package/esm/typings/src/{book-2.0/commitments → commitments}/META_LINK/META_LINK.d.ts +1 -1
- package/esm/typings/src/{book-2.0/commitments → commitments}/MODEL/MODEL.d.ts +1 -1
- package/esm/typings/src/{book-2.0/commitments → commitments}/NOTE/NOTE.d.ts +1 -1
- package/esm/typings/src/{book-2.0/commitments → commitments}/PERSONA/PERSONA.d.ts +1 -1
- package/esm/typings/src/{book-2.0/commitments → commitments}/RULE/RULE.d.ts +1 -1
- package/esm/typings/src/{book-2.0/commitments → commitments}/SAMPLE/SAMPLE.d.ts +1 -1
- package/esm/typings/src/{book-2.0/commitments → commitments}/SCENARIO/SCENARIO.d.ts +1 -1
- package/esm/typings/src/{book-2.0/commitments → commitments}/STYLE/STYLE.d.ts +1 -1
- package/esm/typings/src/{book-2.0/commitments → commitments}/_base/BaseCommitmentDefinition.d.ts +1 -1
- package/esm/typings/src/{book-2.0/commitments → commitments}/_base/CommitmentDefinition.d.ts +1 -1
- package/esm/typings/src/{book-2.0/commitments → commitments}/_base/NotYetImplementedCommitmentDefinition.d.ts +1 -1
- package/esm/typings/src/{book-2.0/commitments → commitments}/_base/createEmptyAgentModelRequirements.d.ts +1 -1
- package/esm/typings/src/execution/LlmExecutionTools.d.ts +1 -1
- package/esm/typings/src/llm-providers/_common/utils/assertUniqueModels.d.ts +12 -0
- package/esm/typings/src/llm-providers/agent/Agent.d.ts +10 -9
- package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +5 -1
- package/esm/typings/src/llm-providers/agent/CreateAgentLlmExecutionToolsOptions.d.ts +1 -1
- package/esm/typings/src/llm-providers/agent/RemoteAgent.d.ts +32 -0
- package/esm/typings/src/llm-providers/agent/RemoteAgentOptions.d.ts +11 -0
- package/esm/typings/src/llm-providers/openai/OpenAiAssistantExecutionTools.d.ts +29 -4
- package/esm/typings/src/llm-providers/openai/openai-models.test.d.ts +4 -0
- package/esm/typings/src/remote-server/startAgentServer.d.ts +1 -1
- package/esm/typings/src/remote-server/startRemoteServer.d.ts +1 -2
- package/esm/typings/src/storage/_common/PromptbookStorage.d.ts +1 -0
- package/esm/typings/src/transpilers/openai-sdk/register.d.ts +1 -1
- package/esm/typings/src/types/typeAliases.d.ts +12 -0
- package/esm/typings/src/utils/color/internal-utils/checkChannelValue.d.ts +0 -3
- package/esm/typings/src/utils/normalization/normalize-to-kebab-case.d.ts +2 -0
- package/esm/typings/src/utils/normalization/normalizeTo_PascalCase.d.ts +3 -0
- package/esm/typings/src/utils/normalization/normalizeTo_camelCase.d.ts +2 -0
- package/esm/typings/src/utils/normalization/titleToName.d.ts +2 -0
- package/esm/typings/src/utils/random/$generateBookBoilerplate.d.ts +2 -2
- package/esm/typings/src/utils/random/$randomFullnameWithColor.d.ts +1 -1
- package/esm/typings/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/umd/index.umd.js +320 -182
- package/umd/index.umd.js.map +1 -1
- package/apps/agents-server/src/supabase/TODO.txt +0 -1
- /package/esm/typings/src/{book-2.0/commitments → commitments}/_base/BookCommitment.d.ts +0 -0
- /package/esm/typings/src/{book-2.0/commitments → commitments}/_base/ParsedCommitment.d.ts +0 -0
- /package/esm/typings/src/{book-2.0/commitments → commitments}/index.d.ts +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { BookEditor } from '@promptbook-local/components';
|
|
4
4
|
import { string_book } from '@promptbook-local/types';
|
|
5
|
-
import { useState } from 'react';
|
|
5
|
+
import { useEffect, useRef, useState } from 'react';
|
|
6
6
|
|
|
7
7
|
type BookEditorWrapperProps = {
|
|
8
8
|
agentName: string;
|
|
@@ -15,23 +15,22 @@ export function BookEditorWrapper({ agentName, initialAgentSource }: BookEditorW
|
|
|
15
15
|
const [agentSource, setAgentSource] = useState<string_book>(initialAgentSource);
|
|
16
16
|
const [saveStatus, setSaveStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle');
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
// Debounce timer ref so we can clear previous pending save
|
|
19
|
+
const debounceTimerRef = useRef<number | null>(null);
|
|
20
|
+
// Configurable debounce delay (ms) - tweak if needed
|
|
21
|
+
const DEBOUNCE_DELAY = 1000;
|
|
21
22
|
|
|
23
|
+
const performSave = async (sourceToSave: string_book) => {
|
|
24
|
+
setSaveStatus('saving');
|
|
22
25
|
try {
|
|
23
26
|
const response = await fetch(`/agents/${encodeURIComponent(agentName)}/api/book`, {
|
|
24
27
|
method: 'PUT',
|
|
25
|
-
headers: {
|
|
26
|
-
|
|
27
|
-
},
|
|
28
|
-
body: newSource,
|
|
28
|
+
headers: { 'Content-Type': 'text/plain' },
|
|
29
|
+
body: sourceToSave,
|
|
29
30
|
});
|
|
30
|
-
|
|
31
31
|
if (!response.ok) {
|
|
32
32
|
throw new Error(`Failed to save: ${response.statusText}`);
|
|
33
33
|
}
|
|
34
|
-
|
|
35
34
|
setSaveStatus('saved');
|
|
36
35
|
setTimeout(() => setSaveStatus('idle'), 2000); // Reset status after 2 seconds
|
|
37
36
|
} catch (error) {
|
|
@@ -41,11 +40,39 @@ export function BookEditorWrapper({ agentName, initialAgentSource }: BookEditorW
|
|
|
41
40
|
}
|
|
42
41
|
};
|
|
43
42
|
|
|
43
|
+
const scheduleSave = (nextSource: string_book) => {
|
|
44
|
+
// Clear existing pending save
|
|
45
|
+
if (debounceTimerRef.current) {
|
|
46
|
+
clearTimeout(debounceTimerRef.current);
|
|
47
|
+
}
|
|
48
|
+
// We stay 'idle' while typing; could add a 'pending' status in future if desired
|
|
49
|
+
// Schedule new save
|
|
50
|
+
debounceTimerRef.current = window.setTimeout(() => {
|
|
51
|
+
performSave(nextSource);
|
|
52
|
+
}, DEBOUNCE_DELAY);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const handleChange = (newSource: string_book) => {
|
|
56
|
+
setAgentSource(newSource);
|
|
57
|
+
scheduleSave(newSource);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Cleanup on unmount to avoid lingering timeouts
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
return () => {
|
|
63
|
+
if (debounceTimerRef.current) {
|
|
64
|
+
clearTimeout(debounceTimerRef.current);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}, []);
|
|
68
|
+
|
|
44
69
|
return (
|
|
45
|
-
<div className="w-full h-
|
|
70
|
+
<div className="w-full h-full">
|
|
46
71
|
{saveStatus !== 'idle' && (
|
|
47
72
|
<div
|
|
48
|
-
|
|
73
|
+
role="status"
|
|
74
|
+
aria-live="polite"
|
|
75
|
+
className={`fixed top-5 right-28 z-50 px-4 py-2 text-sm rounded shadow-md ${
|
|
49
76
|
saveStatus === 'saving'
|
|
50
77
|
? 'bg-blue-100 text-blue-800'
|
|
51
78
|
: saveStatus === 'saved'
|
|
@@ -54,21 +81,60 @@ export function BookEditorWrapper({ agentName, initialAgentSource }: BookEditorW
|
|
|
54
81
|
}`}
|
|
55
82
|
>
|
|
56
83
|
{saveStatus === 'saving' && '💾 Saving...'}
|
|
57
|
-
{saveStatus === 'saved' && '✅ Saved
|
|
84
|
+
{saveStatus === 'saved' && '✅ Saved'}
|
|
58
85
|
{saveStatus === 'error' && '❌ Failed to save'}
|
|
59
86
|
</div>
|
|
60
87
|
)}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
88
|
+
|
|
89
|
+
<BookEditor
|
|
90
|
+
className="w-full h-full"
|
|
91
|
+
isBorderRadiusDisabled
|
|
92
|
+
height={null}
|
|
93
|
+
value={agentSource}
|
|
94
|
+
onChange={handleChange}
|
|
95
|
+
onFileUpload={async (file) => {
|
|
96
|
+
const formData = new FormData();
|
|
97
|
+
formData.append('file', file);
|
|
98
|
+
|
|
99
|
+
const response = await fetch('/api/upload', {
|
|
100
|
+
method: 'POST',
|
|
101
|
+
body: formData,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
if (!response.ok) {
|
|
105
|
+
throw new Error(`Failed to upload file: ${response.statusText}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const { fileUrl: longFileUrl } = await response.json();
|
|
109
|
+
|
|
110
|
+
const LONG_URL = `${process.env.NEXT_PUBLIC_CDN_PUBLIC_URL!}/${process.env
|
|
111
|
+
.NEXT_PUBLIC_CDN_PATH_PREFIX!}/user/files/`;
|
|
112
|
+
const SHORT_URL = `https://ptbk.io/k/`;
|
|
113
|
+
// <- TODO: [🌍] Unite this logic in one place
|
|
114
|
+
|
|
115
|
+
const shortFileUrl = longFileUrl.split(LONG_URL).join(SHORT_URL);
|
|
116
|
+
|
|
117
|
+
console.log(`File uploaded:`, {
|
|
118
|
+
LONG_URL,
|
|
119
|
+
SHORT_URL,
|
|
120
|
+
longFileUrl,
|
|
121
|
+
shortFileUrl,
|
|
122
|
+
file,
|
|
123
|
+
formData,
|
|
124
|
+
response,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
return shortFileUrl;
|
|
128
|
+
}}
|
|
129
|
+
// <- TODO: !!!! Create two-state solution for `<BookEditor onFileUpload={...} />`
|
|
130
|
+
/>
|
|
64
131
|
</div>
|
|
65
132
|
);
|
|
66
133
|
}
|
|
67
134
|
|
|
68
135
|
/**
|
|
69
136
|
* TODO: [🚗] Transfer the saving logic to `<BookEditor/>` be aware of CRDT / yjs approach to be implementable in future
|
|
70
|
-
*
|
|
137
|
+
* DONE: Implement debouncing for auto-save (600ms delay) ✅
|
|
71
138
|
* TODO: !!! Add error handling and retry logic
|
|
72
139
|
* TODO: !!! Show save status indicator
|
|
73
|
-
* TODO: !!!!! Add file upload capability
|
|
74
140
|
*/
|
|
@@ -5,15 +5,19 @@ import { headers } from 'next/headers';
|
|
|
5
5
|
import { $sideEffect } from '../../../../../../../src/utils/organization/$sideEffect';
|
|
6
6
|
import { BookEditorWrapper } from './BookEditorWrapper';
|
|
7
7
|
|
|
8
|
-
export default async function
|
|
8
|
+
export default async function AgentBookPage({ params }: { params: Promise<{ agentName: string }> }) {
|
|
9
9
|
$sideEffect(headers());
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
let { agentName } = await params;
|
|
12
|
+
agentName = decodeURIComponent(agentName);
|
|
12
13
|
const collection = await $provideAgentCollectionForServer();
|
|
13
|
-
const
|
|
14
|
-
const agentSource = agentSourceSubject.getValue();
|
|
14
|
+
const agentSource = await collection.getAgentSource(decodeURIComponent(agentName));
|
|
15
15
|
|
|
16
|
-
return
|
|
16
|
+
return (
|
|
17
|
+
<main className={`w-screen h-screen`}>
|
|
18
|
+
<BookEditorWrapper agentName={agentName} initialAgentSource={agentSource} />
|
|
19
|
+
</main>
|
|
20
|
+
);
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
/**
|
|
@@ -2,15 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
import { ResizablePanelsAuto } from '@common/components/ResizablePanelsAuto/ResizablePanelsAuto';
|
|
4
4
|
import { useStateInLocalStorage } from '@common/hooks/useStateInLocalStorage';
|
|
5
|
-
import {
|
|
5
|
+
import { AgentChat, BookEditor } from '@promptbook-local/components';
|
|
6
6
|
import { Agent, book } from '@promptbook-local/core';
|
|
7
7
|
// import { RemoteLlmExecutionTools } from '@promptbook-local/remote-client';
|
|
8
8
|
import { OpenAiAssistantExecutionTools } from '@promptbook-local/openai';
|
|
9
9
|
import type { string_book } from '@promptbook-local/types';
|
|
10
|
-
import { spaceTrim } from '@promptbook-local/utils';
|
|
11
10
|
import { useMemo, useState } from 'react';
|
|
12
11
|
|
|
13
|
-
export function
|
|
12
|
+
export function AgentBookAndChatComponent() {
|
|
14
13
|
const [apiKey, setApiKey] = useStateInLocalStorage<string>('openai-apiKey', () => '');
|
|
15
14
|
const [isApiKeyVisible, setIsApiKeyVisible] = useState(false);
|
|
16
15
|
const [isApiKeySectionCollapsed, setIsApiKeySectionCollapsed] = useState(!!apiKey);
|
|
@@ -85,11 +84,6 @@ export function SelfLearningBook() {
|
|
|
85
84
|
return agent;
|
|
86
85
|
}, [agentSource, setAgentSource, apiKey]);
|
|
87
86
|
|
|
88
|
-
const llmTools = useMemo(() => {
|
|
89
|
-
const llmTools = agent.getLlmExecutionTools();
|
|
90
|
-
return llmTools;
|
|
91
|
-
}, [agent]);
|
|
92
|
-
|
|
93
87
|
return (
|
|
94
88
|
<div className="min-h-screen relative">
|
|
95
89
|
{/* Floating API Key Configuration */}
|
|
@@ -143,55 +137,18 @@ export function SelfLearningBook() {
|
|
|
143
137
|
<ResizablePanelsAuto name="two-editors">
|
|
144
138
|
<BookEditor
|
|
145
139
|
className="w-full h-full"
|
|
140
|
+
isBorderRadiusDisabled
|
|
146
141
|
height={null}
|
|
147
142
|
value={agentSource}
|
|
148
143
|
onChange={setAgentSource}
|
|
149
144
|
// className={styles.BookEditor}
|
|
150
145
|
isVerbose={false}
|
|
151
|
-
isBorderRadiusDisabled
|
|
152
146
|
onFileUpload={(file) => {
|
|
153
147
|
return file.name;
|
|
154
148
|
}}
|
|
155
149
|
/>
|
|
156
|
-
|
|
157
|
-
<AgentChat className=
|
|
158
|
-
TODO: !!! Move to `AgentChat` component
|
|
159
|
-
*/}
|
|
160
|
-
<LlmChat
|
|
161
|
-
title={`Chat with ${agent.agentName || 'Agent'}`}
|
|
162
|
-
// TODO: !!! Pass persistenceKey="chat-with-pavol-hejny"
|
|
163
|
-
userParticipantName="USER"
|
|
164
|
-
llmParticipantName="AGENT" // <- TODO: [🧠] Maybe dynamic agent id
|
|
165
|
-
initialMessages={[
|
|
166
|
-
{
|
|
167
|
-
from: 'AGENT',
|
|
168
|
-
content: spaceTrim(`
|
|
169
|
-
|
|
170
|
-
Hello! I am ${agent.agentName || 'an AI Agent'}.
|
|
171
|
-
|
|
172
|
-
[Hello](?message=Hello, can you tell me about yourself?)
|
|
173
|
-
`),
|
|
174
|
-
},
|
|
175
|
-
]}
|
|
176
|
-
participants={[
|
|
177
|
-
{
|
|
178
|
-
name: 'AGENT',
|
|
179
|
-
fullname: agent.agentName || 'Agent',
|
|
180
|
-
avatarSrc: agent.meta.image,
|
|
181
|
-
color: agent.meta.color,
|
|
182
|
-
isMe: false,
|
|
183
|
-
agentSource,
|
|
184
|
-
},
|
|
185
|
-
{
|
|
186
|
-
name: 'USER',
|
|
187
|
-
fullname: 'User',
|
|
188
|
-
color: '#115EB6',
|
|
189
|
-
isMe: true,
|
|
190
|
-
},
|
|
191
|
-
]}
|
|
192
|
-
{...{ llmTools }}
|
|
193
|
-
className={`h-full flex flex-col`}
|
|
194
|
-
/>
|
|
150
|
+
|
|
151
|
+
<AgentChat className={`h-full flex flex-col`} {...{ agent }} />
|
|
195
152
|
</ResizablePanelsAuto>
|
|
196
153
|
</div>
|
|
197
154
|
);
|
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
import dynamic from 'next/dynamic';
|
|
4
4
|
|
|
5
|
-
const SelfLearningBook = dynamic(
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
const SelfLearningBook = dynamic(
|
|
6
|
+
() => import('./AgentBookAndChatComponent').then((module) => module.AgentBookAndChatComponent),
|
|
7
|
+
{
|
|
8
|
+
ssr: false,
|
|
9
|
+
},
|
|
10
|
+
);
|
|
8
11
|
|
|
9
|
-
export default function
|
|
12
|
+
export default function AgentBookAndChatPage() {
|
|
10
13
|
return <SelfLearningBook />;
|
|
11
14
|
}
|
|
12
15
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { usePromise } from '@common/hooks/usePromise';
|
|
4
|
+
import { AgentChat } from '@promptbook-local/components';
|
|
5
|
+
import { RemoteAgent } from '@promptbook-local/core';
|
|
6
|
+
import { useMemo } from 'react';
|
|
7
|
+
import { string_agent_url } from '../../../../../../../src/types/typeAliases';
|
|
8
|
+
|
|
9
|
+
type AgentChatWrapperProps = {
|
|
10
|
+
agentUrl: string_agent_url;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// TODO: !!!! Rename to AgentChatSomethingWrapper
|
|
14
|
+
|
|
15
|
+
export function AgentChatWrapper(props: AgentChatWrapperProps) {
|
|
16
|
+
const { agentUrl } = props;
|
|
17
|
+
|
|
18
|
+
const agentPromise = useMemo(
|
|
19
|
+
() =>
|
|
20
|
+
RemoteAgent.connect({
|
|
21
|
+
agentUrl,
|
|
22
|
+
isVerbose: true,
|
|
23
|
+
}),
|
|
24
|
+
[agentUrl],
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const { value: agent } = usePromise(agentPromise, [agentPromise]);
|
|
28
|
+
|
|
29
|
+
if (!agent) {
|
|
30
|
+
return <>{/* <- TODO: !!! <PromptbookLoading /> */}</>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return <AgentChat className={`w-full h-full`} agent={agent} />;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* TODO: [🚗] Transfer the saving logic to `<BookEditor/>` be aware of CRDT / yjs approach to be implementable in future
|
|
38
|
+
*/
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use server';
|
|
2
|
+
|
|
3
|
+
import { headers } from 'next/headers';
|
|
4
|
+
import { $sideEffect } from '../../../../../../../src/utils/organization/$sideEffect';
|
|
5
|
+
import { AgentChatWrapper } from './AgentChatWrapper';
|
|
6
|
+
|
|
7
|
+
export default async function AgentChatPage({ params }: { params: Promise<{ agentName: string }> }) {
|
|
8
|
+
$sideEffect(headers());
|
|
9
|
+
let { agentName } = await params;
|
|
10
|
+
agentName = decodeURIComponent(agentName);
|
|
11
|
+
|
|
12
|
+
const agentUrl = `/agents/${agentName}`;
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<main className={`w-screen h-screen`}>
|
|
16
|
+
<AgentChatWrapper agentUrl={agentUrl} />
|
|
17
|
+
</main>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* TODO: [🚗] Components and pages here should be just tiny UI wraper around proper agent logic and conponents
|
|
23
|
+
*/
|
|
@@ -18,14 +18,15 @@ export default async function AgentPage({ params }: { params: Promise<{ agentNam
|
|
|
18
18
|
|
|
19
19
|
$sideEffect(headers());
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
let { agentName } = await params;
|
|
22
|
+
agentName = decodeURIComponent(agentName);
|
|
23
|
+
|
|
22
24
|
const collection = await $provideAgentCollectionForServer();
|
|
23
|
-
const
|
|
24
|
-
const agentSource = agentSourceSubject.getValue();
|
|
25
|
+
const agentSource = await collection.getAgentSource(agentName);
|
|
25
26
|
const agentProfile = parseAgentSource(agentSource);
|
|
26
27
|
|
|
27
28
|
// Build agent page URL for QR and copy
|
|
28
|
-
const pageUrl =
|
|
29
|
+
const pageUrl = `${process.env.NEXT_PUBLIC_URL}/agents/${encodeURIComponent(agentName)}`;
|
|
29
30
|
// <- TODO: !!! Better
|
|
30
31
|
|
|
31
32
|
// Extract brand color from meta
|
|
@@ -126,14 +127,14 @@ export default async function AgentPage({ params }: { params: Promise<{ agentNam
|
|
|
126
127
|
<div className="flex gap-4 mt-6">
|
|
127
128
|
<a
|
|
128
129
|
href={`${pageUrl}/chat`}
|
|
129
|
-
// <-
|
|
130
|
+
// <- TODO: [🧠] Can I append path like this on current browser URL in href?
|
|
130
131
|
className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded shadow font-semibold transition"
|
|
131
132
|
>
|
|
132
133
|
💬 Chat
|
|
133
134
|
</a>
|
|
134
135
|
<a
|
|
135
136
|
href={`${pageUrl}/book`}
|
|
136
|
-
// <-
|
|
137
|
+
// <- TODO: [🧠] Can I append path like this on current browser URL in href?
|
|
137
138
|
className="bg-gray-200 hover:bg-gray-300 text-gray-800 px-4 py-2 rounded shadow font-semibold transition"
|
|
138
139
|
>
|
|
139
140
|
✏️ Edit Agent Book
|
|
@@ -157,4 +158,5 @@ export default async function AgentPage({ params }: { params: Promise<{ agentNam
|
|
|
157
158
|
* TODO: !!! Make this page look nice - 🃏
|
|
158
159
|
* TODO: !!! Show usage of LLM
|
|
159
160
|
* TODO: [🚗] Components and pages here should be just tiny UI wraper around proper agent logic and conponents
|
|
161
|
+
* TODO: [🎣][🧠] Maybe do API / Page for transpilers, Allow to export each agent
|
|
160
162
|
*/
|
|
@@ -39,6 +39,10 @@ export async function GET(request: Request) {
|
|
|
39
39
|
|
|
40
40
|
return new Response(readableStream, {
|
|
41
41
|
status: 200,
|
|
42
|
-
headers: { 'Content-Type': 'text/
|
|
42
|
+
headers: { 'Content-Type': 'text/markdown' },
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Note: [🐚] This is how streaming is implemented correctly
|
|
48
|
+
*/
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { nextRequestToNodeRequest } from '@/src/utils/cdn/utils/nextRequestToNodeRequest';
|
|
2
|
+
import { TODO_any } from '@promptbook-local/types';
|
|
3
|
+
import { serializeError } from '@promptbook-local/utils';
|
|
4
|
+
import formidable from 'formidable';
|
|
5
|
+
import { readFile } from 'fs/promises';
|
|
6
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
7
|
+
import { assertsError } from '../../../../../../src/errors/assertsError';
|
|
8
|
+
import { string_url } from '../../../../../../src/types/typeAliases';
|
|
9
|
+
import { keepUnused } from '../../../../../../src/utils/organization/keepUnused';
|
|
10
|
+
import { $provideCdnForServer } from '../../../../src/tools/$provideCdnForServer';
|
|
11
|
+
import { getUserFileCdnKey } from '../../../../src/utils/cdn/utils/getUserFileCdnKey';
|
|
12
|
+
import { validateMimeType } from '../../../../src/utils/validators/validateMimeType';
|
|
13
|
+
|
|
14
|
+
export async function POST(request: NextRequest) {
|
|
15
|
+
try {
|
|
16
|
+
const nodeRequest = await nextRequestToNodeRequest(request);
|
|
17
|
+
|
|
18
|
+
const files = await new Promise<formidable.Files>((resolve, reject) => {
|
|
19
|
+
const form = formidable({});
|
|
20
|
+
form.parse(nodeRequest as TODO_any, (error, fields, files) => {
|
|
21
|
+
keepUnused(fields);
|
|
22
|
+
|
|
23
|
+
if (error) {
|
|
24
|
+
return reject(error);
|
|
25
|
+
}
|
|
26
|
+
resolve(files);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const uploadedFiles = files.file;
|
|
31
|
+
|
|
32
|
+
if (!uploadedFiles || uploadedFiles.length !== 1) {
|
|
33
|
+
return NextResponse.json(
|
|
34
|
+
{ message: 'In form data there is not EXACTLY one "file" field' },
|
|
35
|
+
{ status: 400 },
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const uploadedFile = uploadedFiles[0]!;
|
|
40
|
+
const fileBuffer = await readFile(uploadedFile.filepath);
|
|
41
|
+
const cdn = $provideCdnForServer();
|
|
42
|
+
const key = getUserFileCdnKey(fileBuffer, uploadedFile.originalFilename || uploadedFile.newFilename);
|
|
43
|
+
|
|
44
|
+
await cdn.setItem(key, {
|
|
45
|
+
type: validateMimeType(uploadedFile.mimetype),
|
|
46
|
+
data: fileBuffer,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const fileUrl = cdn.getItemUrl(key);
|
|
50
|
+
|
|
51
|
+
return NextResponse.json({ fileUrl: fileUrl.href as string_url }, { status: 201 });
|
|
52
|
+
} catch (error) {
|
|
53
|
+
assertsError(error);
|
|
54
|
+
|
|
55
|
+
console.error(error);
|
|
56
|
+
|
|
57
|
+
return new Response(
|
|
58
|
+
JSON.stringify(
|
|
59
|
+
serializeError(error),
|
|
60
|
+
// <- TODO: !!! Rename `serializeError` to `errorToJson`
|
|
61
|
+
null,
|
|
62
|
+
4,
|
|
63
|
+
// <- TODO: !!! Allow to configure pretty print for agent server
|
|
64
|
+
),
|
|
65
|
+
{
|
|
66
|
+
status: 400, // <- TODO: !!! Make `errorToHttpStatusCode`
|
|
67
|
+
headers: { 'Content-Type': 'application/json' },
|
|
68
|
+
},
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { AgentsDatabaseSchema } from '@promptbook-local/types';
|
|
2
2
|
import { $isRunningInBrowser, $isRunningInNode, $isRunningInWebWorker } from '@promptbook-local/utils';
|
|
3
3
|
import type { SupabaseClient } from '@supabase/supabase-js';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { $provideSupabaseForBrowser } from './$provideSupabaseForBrowser';
|
|
5
|
+
import { $provideSupabaseForServer } from './$provideSupabaseForServer';
|
|
6
|
+
import { $provideSupabaseForWorker } from './$provideSupabaseForWorker';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Get supabase client in any environment
|
|
@@ -12,14 +12,18 @@ import { getSupabaseForWorker } from './getSupabaseForWorker';
|
|
|
12
12
|
*
|
|
13
13
|
* @returns instance of supabase client
|
|
14
14
|
*/
|
|
15
|
-
export function
|
|
15
|
+
export function $provideSupabase(): SupabaseClient<AgentsDatabaseSchema> {
|
|
16
16
|
if ($isRunningInNode()) {
|
|
17
|
-
return
|
|
17
|
+
return $provideSupabaseForServer();
|
|
18
18
|
} else if ($isRunningInBrowser()) {
|
|
19
|
-
return
|
|
19
|
+
return $provideSupabaseForBrowser();
|
|
20
20
|
} else if ($isRunningInWebWorker()) {
|
|
21
|
-
return
|
|
21
|
+
return $provideSupabaseForWorker();
|
|
22
22
|
} else {
|
|
23
23
|
throw new Error('Unknown environment, cannot determine how to get Supabase client');
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Note: [🎇] Promptbook Agents Server is not using Supabase in Browser so maybe remove this file
|
|
29
|
+
*/
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { createClient, SupabaseClient } from '@supabase/supabase-js';
|
|
2
1
|
import { AgentsDatabaseSchema } from '@promptbook-local/types';
|
|
3
2
|
import { $isRunningInBrowser } from '@promptbook-local/utils';
|
|
3
|
+
import { createClient, SupabaseClient } from '@supabase/supabase-js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Internal cache for `
|
|
6
|
+
* Internal cache for `$provideSupabaseForBrowser`
|
|
7
7
|
*
|
|
8
8
|
* @private
|
|
9
9
|
* @singleton
|
|
@@ -14,14 +14,14 @@ let supabase: SupabaseClient<AgentsDatabaseSchema>;
|
|
|
14
14
|
* Get supabase client
|
|
15
15
|
*
|
|
16
16
|
* Note: The client is cached, so it's safe to call this function multiple times
|
|
17
|
-
* Note: This function is available ONLY in browser, use
|
|
17
|
+
* Note: This function is available ONLY in browser, use $provideSupabaseForServer in node
|
|
18
18
|
*
|
|
19
19
|
* @returns instance of supabase client
|
|
20
20
|
*/
|
|
21
|
-
export function
|
|
21
|
+
export function $provideSupabaseForBrowser(): typeof supabase {
|
|
22
22
|
if (!$isRunningInBrowser()) {
|
|
23
23
|
throw new Error(
|
|
24
|
-
'Function `
|
|
24
|
+
'Function `$provideSupabaseForBrowser` can not be used on server or worker, use `$provideSupabaseForServer` or `$provideSupabaseForWorker` instead.',
|
|
25
25
|
);
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -35,3 +35,7 @@ export function getSupabaseForBrowser(): typeof supabase {
|
|
|
35
35
|
|
|
36
36
|
return supabase;
|
|
37
37
|
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Note: [🎇] Promptbook Agents Server is not using Supabase in Browser so maybe remove this file
|
|
41
|
+
*/
|
|
@@ -3,7 +3,7 @@ import { $isRunningInNode } from '@promptbook-local/utils';
|
|
|
3
3
|
import { createClient, SupabaseClient } from '@supabase/supabase-js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Internal cache for `
|
|
6
|
+
* Internal cache for `$provideSupabaseForServer`
|
|
7
7
|
*
|
|
8
8
|
* @private
|
|
9
9
|
* @singleton
|
|
@@ -14,14 +14,14 @@ let supabase: SupabaseClient<AgentsDatabaseSchema>;
|
|
|
14
14
|
* Get supabase client
|
|
15
15
|
*
|
|
16
16
|
* Note: The client is cached, so it's safe to call this function multiple times
|
|
17
|
-
* Note: This function is available ONLY on server/node, use
|
|
17
|
+
* Note: This function is available ONLY on server/node, use $provideSupabaseForClient in browser
|
|
18
18
|
*
|
|
19
19
|
* @returns instance of supabase client
|
|
20
20
|
*/
|
|
21
|
-
export function
|
|
21
|
+
export function $provideSupabaseForServer(): typeof supabase {
|
|
22
22
|
if (!$isRunningInNode()) {
|
|
23
23
|
throw new Error(
|
|
24
|
-
'Function `
|
|
24
|
+
'Function `$provideSupabaseForServer` can not be used in browser, use `$provideSupabaseForBrowser` instead.',
|
|
25
25
|
);
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -3,7 +3,7 @@ import { $isRunningInWebWorker } from '@promptbook-local/utils';
|
|
|
3
3
|
import { createClient, SupabaseClient } from '@supabase/supabase-js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Internal cache for `
|
|
6
|
+
* Internal cache for `$provideSupabaseForWorker`
|
|
7
7
|
*
|
|
8
8
|
* @private
|
|
9
9
|
* @singleton
|
|
@@ -14,14 +14,14 @@ let supabase: SupabaseClient<AgentsDatabaseSchema>;
|
|
|
14
14
|
* Get supabase client
|
|
15
15
|
*
|
|
16
16
|
* Note: The client is cached, so it's safe to call this function multiple times
|
|
17
|
-
* Note: This function is available ONLY in worker, use
|
|
17
|
+
* Note: This function is available ONLY in worker, use $provideSupabaseForBrowser for main thread
|
|
18
18
|
*
|
|
19
19
|
* @returns instance of supabase client
|
|
20
20
|
*/
|
|
21
|
-
export function
|
|
21
|
+
export function $provideSupabaseForWorker(): typeof supabase {
|
|
22
22
|
if (!$isRunningInWebWorker) {
|
|
23
23
|
throw new Error(
|
|
24
|
-
'Function `
|
|
24
|
+
'Function `$provideSupabaseForWorker` can not be used in browser, use `$provideSupabaseForBrowser` instead.',
|
|
25
25
|
);
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -37,6 +37,7 @@ export function getSupabaseForWorker(): typeof supabase {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
|
+
* Note: [🎇] Promptbook Agents Server is not using Supabase in Browser so maybe remove this file
|
|
40
41
|
* TODO: Fix> No storage option exists to persist the session, which may result in unexpected behavior when using auth.
|
|
41
42
|
If you want to set persistSession to true, please provide a storage option or you may set persistSession to false to disable this warning.
|
|
42
43
|
*/
|