@promptbook/cli 0.103.0-46 → 0.103.0-47
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/config.ts.todo +4 -4
- 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 +6 -4
- 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 +83 -17
- 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 -46
- 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 +6 -4
- 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 +75 -0
- package/apps/agents-server/src/tools/$provideAgentCollectionForServer.ts +1 -0
- package/apps/agents-server/src/tools/$provideCdnForServer.ts +28 -0
- 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 +27 -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 +154 -161
- 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 +16 -14
- package/esm/typings/src/_packages/types.index.d.ts +12 -6
- 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 +1 -1
- package/esm/typings/src/book-2.0/agent-source/createCommitmentRegex.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/{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/agent/Agent.d.ts +3 -7
- package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +1 -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 +5 -1
- package/esm/typings/src/storage/_common/PromptbookStorage.d.ts +1 -0
- package/esm/typings/src/types/typeAliases.d.ts +6 -0
- package/esm/typings/src/utils/color/internal-utils/checkChannelValue.d.ts +0 -3
- 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 +154 -161
- package/umd/index.umd.js.map +1 -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
|
@@ -187,21 +187,21 @@ export const AZURE_COMPUTER_VISION_KEY = config.get('AZURE_COMPUTER_VISION_KEY')
|
|
|
187
187
|
|
|
188
188
|
// TODO: [🧠] How to do required only on server
|
|
189
189
|
export const CDN_BUCKET = config.get('CDN_BUCKET') /*.required([📐])*/.value;
|
|
190
|
-
export const
|
|
190
|
+
export const NEXT_PUBLIC_CDN_PATH_PREFIX = config.get('NEXT_PUBLIC_CDN_PATH_PREFIX') /*.required([📐])*/.value;
|
|
191
191
|
export const CDN_ENDPOINT = config.get('CDN_ENDPOINT') /*.required([📐])*/.value;
|
|
192
192
|
export const CDN_ACCESS_KEY_ID = config.get('CDN_ACCESS_KEY_ID') /*.required([📐])*/.value;
|
|
193
193
|
export const CDN_SECRET_ACCESS_KEY = config.get('CDN_SECRET_ACCESS_KEY') /*.required([📐])*/.value;
|
|
194
|
-
export const
|
|
194
|
+
export const NEXT_PUBLIC_CDN_PUBLIC_URL = config.get('NEXT_PUBLIC_CDN_PUBLIC_URL').url() /*.required([📐])*/.value;
|
|
195
195
|
|
|
196
196
|
export const CDN = (CDN_BUCKET &&
|
|
197
197
|
// [📐]
|
|
198
198
|
new DigitalOceanSpaces({
|
|
199
199
|
bucket: CDN_BUCKET!,
|
|
200
|
-
pathPrefix:
|
|
200
|
+
pathPrefix: NEXT_PUBLIC_CDN_PATH_PREFIX!,
|
|
201
201
|
endpoint: CDN_ENDPOINT!,
|
|
202
202
|
accessKeyId: CDN_ACCESS_KEY_ID!,
|
|
203
203
|
secretAccessKey: CDN_SECRET_ACCESS_KEY!,
|
|
204
|
-
cdnPublicUrl:
|
|
204
|
+
cdnPublicUrl: NEXT_PUBLIC_CDN_PUBLIC_URL!,
|
|
205
205
|
gzip: false /* <- TODO: Maybe just remove this functionality from WebGPT repository */,
|
|
206
206
|
})) as DigitalOceanSpaces;
|
|
207
207
|
|
|
@@ -14,7 +14,7 @@ export async function GET(request: Request, { params }: { params: Promise<{ agen
|
|
|
14
14
|
const collection = await $provideAgentCollectionForServer();
|
|
15
15
|
const agentSource = await collection.getAgentSource(agentName);
|
|
16
16
|
|
|
17
|
-
return new Response(agentSource
|
|
17
|
+
return new Response(agentSource, {
|
|
18
18
|
status: 200,
|
|
19
19
|
headers: { 'Content-Type': 'text/plain' /* <- TODO: [🎳] Mime type of book */ },
|
|
20
20
|
});
|
|
@@ -15,7 +15,7 @@ Content-Type: text/plain
|
|
|
15
15
|
|
|
16
16
|
Gabriel Gray
|
|
17
17
|
|
|
18
|
-
META COLOR #
|
|
18
|
+
META COLOR #c9c9c9
|
|
19
19
|
PERSONA Friendly and helpful AI agent.
|
|
20
20
|
RULE Be kind and respectful.
|
|
21
21
|
|
|
@@ -32,6 +32,6 @@ Content-Type: text/plain
|
|
|
32
32
|
|
|
33
33
|
Gabriel Gray
|
|
34
34
|
|
|
35
|
-
META COLOR #
|
|
35
|
+
META COLOR #242424
|
|
36
36
|
PERSONA Strict and professional AI agent.
|
|
37
37
|
RULE Be strict and professional.
|
|
@@ -5,7 +5,8 @@ import { serializeError } from '@promptbook-local/utils';
|
|
|
5
5
|
import { assertsError } from '../../../../../../../../src/errors/assertsError';
|
|
6
6
|
|
|
7
7
|
export async function GET(request: Request, { params }: { params: Promise<{ agentName: string }> }) {
|
|
8
|
-
|
|
8
|
+
let { agentName } = await params;
|
|
9
|
+
agentName = decodeURIComponent(agentName);
|
|
9
10
|
const { searchParams } = new URL(request.url);
|
|
10
11
|
const message = searchParams.get('message') || 'Tell me more about yourself.';
|
|
11
12
|
// <- TODO: !!!! To configuration DEFAULT_INITIAL_HIDDEN_MESSAGE
|
|
@@ -23,9 +24,8 @@ export async function GET(request: Request, { params }: { params: Promise<{ agen
|
|
|
23
24
|
},
|
|
24
25
|
agentSource,
|
|
25
26
|
});
|
|
26
|
-
const llmTools = agent.getLlmExecutionTools();
|
|
27
27
|
|
|
28
|
-
const response = await
|
|
28
|
+
const response = await agent.callChatModel!({
|
|
29
29
|
title: `Chat with agent ${agentName}`,
|
|
30
30
|
parameters: {},
|
|
31
31
|
modelRequirements: {
|
|
@@ -34,9 +34,11 @@ export async function GET(request: Request, { params }: { params: Promise<{ agen
|
|
|
34
34
|
content: message,
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
+
// TODO: [🐚] Implement streaming
|
|
38
|
+
|
|
37
39
|
return new Response(response.content, {
|
|
38
40
|
status: 200,
|
|
39
|
-
headers: { 'Content-Type': 'text/
|
|
41
|
+
headers: { 'Content-Type': 'text/markdown' },
|
|
40
42
|
});
|
|
41
43
|
} catch (error) {
|
|
42
44
|
assertsError(error);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
TODO: [🎣][🧠] Maybe do API / Page for transpilers, Allow to export each agent
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
|
|
2
|
+
import { createAgentModelRequirements } from '@promptbook-local/core';
|
|
3
|
+
import { serializeError } from '@promptbook-local/utils';
|
|
4
|
+
import { assertsError } from '../../../../../../../../src/errors/assertsError';
|
|
5
|
+
import { keepUnused } from '../../../../../../../../src/utils/organization/keepUnused';
|
|
6
|
+
|
|
7
|
+
export async function GET(request: Request, { params }: { params: Promise<{ agentName: string }> }) {
|
|
8
|
+
keepUnused(request /* <- Note: We dont need `request` parameter */);
|
|
9
|
+
let { agentName } = await params;
|
|
10
|
+
agentName = decodeURIComponent(agentName);
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const collection = await $provideAgentCollectionForServer();
|
|
14
|
+
const agentSource = await collection.getAgentSource(agentName);
|
|
15
|
+
const modelRequirements = await createAgentModelRequirements(agentSource);
|
|
16
|
+
|
|
17
|
+
return new Response(
|
|
18
|
+
JSON.stringify(
|
|
19
|
+
modelRequirements,
|
|
20
|
+
// <- TODO: !!! Rename `serializeError` to `errorToJson`
|
|
21
|
+
null,
|
|
22
|
+
4,
|
|
23
|
+
// <- TODO: !!! Allow to configure pretty print for agent server
|
|
24
|
+
),
|
|
25
|
+
{
|
|
26
|
+
status: 200,
|
|
27
|
+
headers: { 'Content-Type': 'application/json' },
|
|
28
|
+
},
|
|
29
|
+
);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
assertsError(error);
|
|
32
|
+
|
|
33
|
+
console.error(error);
|
|
34
|
+
|
|
35
|
+
return new Response(
|
|
36
|
+
JSON.stringify(
|
|
37
|
+
serializeError(error),
|
|
38
|
+
// <- TODO: !!! Rename `serializeError` to `errorToJson`
|
|
39
|
+
null,
|
|
40
|
+
4,
|
|
41
|
+
// <- TODO: !!! Allow to configure pretty print for agent server
|
|
42
|
+
),
|
|
43
|
+
{
|
|
44
|
+
status: 400, // <- TODO: !!! Make `errorToHttpStatusCode`
|
|
45
|
+
headers: { 'Content-Type': 'application/json' },
|
|
46
|
+
},
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* TODO: [🍞] DRY - Make some common utility for API on one agent
|
|
53
|
+
*/
|
package/apps/agents-server/src/app/agents/[agentName]/api/modelRequirements/systemMessage/route.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
|
|
2
|
+
import { createAgentModelRequirements } from '@promptbook-local/core';
|
|
3
|
+
import { serializeError } from '@promptbook-local/utils';
|
|
4
|
+
import { assertsError } from '../../../../../../../../../src/errors/assertsError';
|
|
5
|
+
import { keepUnused } from '../../../../../../../../../src/utils/organization/keepUnused';
|
|
6
|
+
|
|
7
|
+
export async function GET(request: Request, { params }: { params: Promise<{ agentName: string }> }) {
|
|
8
|
+
keepUnused(request /* <- Note: We dont need `request` parameter */);
|
|
9
|
+
let { agentName } = await params;
|
|
10
|
+
agentName = decodeURIComponent(agentName);
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const collection = await $provideAgentCollectionForServer();
|
|
14
|
+
const agentSource = await collection.getAgentSource(agentName);
|
|
15
|
+
const modelRequirements = await createAgentModelRequirements(agentSource);
|
|
16
|
+
const { systemMessage } = modelRequirements;
|
|
17
|
+
|
|
18
|
+
return new Response(systemMessage, {
|
|
19
|
+
status: 200,
|
|
20
|
+
headers: { 'Content-Type': 'text/markdown' },
|
|
21
|
+
});
|
|
22
|
+
} catch (error) {
|
|
23
|
+
assertsError(error);
|
|
24
|
+
|
|
25
|
+
console.error(error);
|
|
26
|
+
|
|
27
|
+
return new Response(
|
|
28
|
+
JSON.stringify(
|
|
29
|
+
serializeError(error),
|
|
30
|
+
// <- TODO: !!! Rename `serializeError` to `errorToJson`
|
|
31
|
+
null,
|
|
32
|
+
4,
|
|
33
|
+
// <- TODO: !!! Allow to configure pretty print for agent server
|
|
34
|
+
),
|
|
35
|
+
{
|
|
36
|
+
status: 400, // <- TODO: !!! Make `errorToHttpStatusCode`
|
|
37
|
+
headers: { 'Content-Type': 'application/json' },
|
|
38
|
+
},
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* TODO: [🍞] DRY - Make some common utility for API on one agent
|
|
45
|
+
*/
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
|
|
2
|
+
import { parseAgentSource } from '@promptbook-local/core';
|
|
3
|
+
import { serializeError } from '@promptbook-local/utils';
|
|
4
|
+
import { assertsError } from '../../../../../../../../src/errors/assertsError';
|
|
5
|
+
import { keepUnused } from '../../../../../../../../src/utils/organization/keepUnused';
|
|
6
|
+
|
|
7
|
+
export async function GET(request: Request, { params }: { params: Promise<{ agentName: string }> }) {
|
|
8
|
+
keepUnused(request /* <- Note: We dont need `request` parameter */);
|
|
9
|
+
let { agentName } = await params;
|
|
10
|
+
agentName = decodeURIComponent(agentName);
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const collection = await $provideAgentCollectionForServer();
|
|
14
|
+
const agentSource = await collection.getAgentSource(agentName);
|
|
15
|
+
const agentProfile = parseAgentSource(agentSource);
|
|
16
|
+
|
|
17
|
+
return new Response(
|
|
18
|
+
JSON.stringify(
|
|
19
|
+
agentProfile,
|
|
20
|
+
// <- TODO: !!! Rename `serializeError` to `errorToJson`
|
|
21
|
+
null,
|
|
22
|
+
4,
|
|
23
|
+
// <- TODO: !!! Allow to configure pretty print for agent server
|
|
24
|
+
),
|
|
25
|
+
{
|
|
26
|
+
status: 200,
|
|
27
|
+
headers: { 'Content-Type': 'application/json' },
|
|
28
|
+
},
|
|
29
|
+
);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
assertsError(error);
|
|
32
|
+
|
|
33
|
+
console.error(error);
|
|
34
|
+
|
|
35
|
+
return new Response(
|
|
36
|
+
JSON.stringify(
|
|
37
|
+
serializeError(error),
|
|
38
|
+
// <- TODO: !!! Rename `serializeError` to `errorToJson`
|
|
39
|
+
null,
|
|
40
|
+
4,
|
|
41
|
+
// <- TODO: !!! Allow to configure pretty print for agent server
|
|
42
|
+
),
|
|
43
|
+
{
|
|
44
|
+
status: 400, // <- TODO: !!! Make `errorToHttpStatusCode`
|
|
45
|
+
headers: { 'Content-Type': 'application/json' },
|
|
46
|
+
},
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* TODO: [🍞] DRY - Make some common utility for API on one agent
|
|
54
|
+
*/
|
|
@@ -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 = 600;
|
|
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,20 +81,59 @@ 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
|
+
height={null}
|
|
92
|
+
value={agentSource}
|
|
93
|
+
onChange={handleChange}
|
|
94
|
+
onFileUpload={async (file) => {
|
|
95
|
+
const formData = new FormData();
|
|
96
|
+
formData.append('file', file);
|
|
97
|
+
|
|
98
|
+
const response = await fetch('/api/upload', {
|
|
99
|
+
method: 'POST',
|
|
100
|
+
body: formData,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
if (!response.ok) {
|
|
104
|
+
throw new Error(`Failed to upload file: ${response.statusText}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const { fileUrl: longFileUrl } = await response.json();
|
|
108
|
+
|
|
109
|
+
const LONG_URL = `${process.env.NEXT_PUBLIC_CDN_PUBLIC_URL!}/${process.env
|
|
110
|
+
.NEXT_PUBLIC_CDN_PATH_PREFIX!}/user/files/`;
|
|
111
|
+
const SHORT_URL = `https://ptbk.io/k/`;
|
|
112
|
+
// <- TODO: [🌍] Unite this logic in one place
|
|
113
|
+
|
|
114
|
+
const shortFileUrl = longFileUrl.split(LONG_URL).join(SHORT_URL);
|
|
115
|
+
|
|
116
|
+
console.log(`File uploaded:`, {
|
|
117
|
+
LONG_URL,
|
|
118
|
+
SHORT_URL,
|
|
119
|
+
longFileUrl,
|
|
120
|
+
shortFileUrl,
|
|
121
|
+
file,
|
|
122
|
+
formData,
|
|
123
|
+
response,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
return shortFileUrl;
|
|
127
|
+
}}
|
|
128
|
+
// <- TODO: !!!! Create two-state solution for `<BookEditor onFileUpload={...} />`
|
|
129
|
+
/>
|
|
64
130
|
</div>
|
|
65
131
|
);
|
|
66
132
|
}
|
|
67
133
|
|
|
68
134
|
/**
|
|
69
135
|
* TODO: [🚗] Transfer the saving logic to `<BookEditor/>` be aware of CRDT / yjs approach to be implementable in future
|
|
70
|
-
*
|
|
136
|
+
* DONE: Implement debouncing for auto-save (600ms delay) ✅
|
|
71
137
|
* TODO: !!! Add error handling and retry logic
|
|
72
138
|
* TODO: !!! Show save status indicator
|
|
73
139
|
* TODO: !!!!! Add file upload capability
|
|
@@ -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,10 +84,7 @@ export function SelfLearningBook() {
|
|
|
85
84
|
return agent;
|
|
86
85
|
}, [agentSource, setAgentSource, apiKey]);
|
|
87
86
|
|
|
88
|
-
|
|
89
|
-
const llmTools = agent.getLlmExecutionTools();
|
|
90
|
-
return llmTools;
|
|
91
|
-
}, [agent]);
|
|
87
|
+
|
|
92
88
|
|
|
93
89
|
return (
|
|
94
90
|
<div className="min-h-screen relative">
|
|
@@ -153,45 +149,8 @@ export function SelfLearningBook() {
|
|
|
153
149
|
return file.name;
|
|
154
150
|
}}
|
|
155
151
|
/>
|
|
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
|
-
/>
|
|
152
|
+
|
|
153
|
+
<AgentChat className={`h-full flex flex-col`} {...{ agent }} />
|
|
195
154
|
</ResizablePanelsAuto>
|
|
196
155
|
</div>
|
|
197
156
|
);
|
|
@@ -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
|
|
@@ -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
|
+
*/
|