@looopy-ai/aws 2.1.13 → 2.1.15
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.
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import { getLogger
|
|
1
|
+
import { getLogger } from '@looopy-ai/core';
|
|
2
2
|
import { Hono as BaseHono } from 'hono';
|
|
3
3
|
import { requestId } from 'hono/request-id';
|
|
4
|
-
import {
|
|
5
|
-
const promptValidator = z.looseObject({
|
|
6
|
-
prompt: z.string().min(1),
|
|
7
|
-
});
|
|
4
|
+
import { handlePrompt } from './invocations/prompt';
|
|
8
5
|
export const hono = (config) => {
|
|
9
6
|
const app = new BaseHono();
|
|
10
7
|
app.use(requestId());
|
|
@@ -77,50 +74,20 @@ export const hono = (config) => {
|
|
|
77
74
|
return processRequest(c, async (agent, authContext, logger) => {
|
|
78
75
|
state.busy = true;
|
|
79
76
|
const body = await c.req.json();
|
|
80
|
-
|
|
81
|
-
if (!promptValidation.success) {
|
|
77
|
+
if (!body || typeof body !== 'object') {
|
|
82
78
|
state.busy = false;
|
|
83
|
-
return c.json({ error: 'Invalid
|
|
79
|
+
return c.json({ error: 'Invalid request body' }, 400);
|
|
80
|
+
}
|
|
81
|
+
switch (body.type) {
|
|
82
|
+
case 'prompt':
|
|
83
|
+
return handlePrompt(agent, body, authContext, c.res, logger, () => {
|
|
84
|
+
state.busy = false;
|
|
85
|
+
}, () => {
|
|
86
|
+
state.busy = false;
|
|
87
|
+
});
|
|
88
|
+
default:
|
|
89
|
+
return c.json({ error: 'Unsupported invocation type' }, 400);
|
|
84
90
|
}
|
|
85
|
-
const { prompt, ...metadata } = promptValidation.data;
|
|
86
|
-
const sseServer = new SSEServer();
|
|
87
|
-
const turn = await agent.startTurn(prompt, { authContext, metadata });
|
|
88
|
-
turn.subscribe({
|
|
89
|
-
next: (evt) => {
|
|
90
|
-
sseServer.emit(agent.contextId, evt);
|
|
91
|
-
},
|
|
92
|
-
complete: async () => {
|
|
93
|
-
sseServer.shutdown();
|
|
94
|
-
state.busy = false;
|
|
95
|
-
},
|
|
96
|
-
});
|
|
97
|
-
const res = c.res;
|
|
98
|
-
logger.info('SSE connection established');
|
|
99
|
-
const stream = new ReadableStream({
|
|
100
|
-
start(controller) {
|
|
101
|
-
sseServer.subscribe({
|
|
102
|
-
setHeader: (name, value) => {
|
|
103
|
-
res.headers.set(name, value);
|
|
104
|
-
},
|
|
105
|
-
write: (chunk) => {
|
|
106
|
-
controller.enqueue(new TextEncoder().encode(chunk));
|
|
107
|
-
},
|
|
108
|
-
end: function () {
|
|
109
|
-
logger.info('SSE stream finished');
|
|
110
|
-
this.writable = false;
|
|
111
|
-
controller.close();
|
|
112
|
-
},
|
|
113
|
-
}, {
|
|
114
|
-
contextId: agent.contextId,
|
|
115
|
-
}, undefined);
|
|
116
|
-
},
|
|
117
|
-
cancel: () => {
|
|
118
|
-
logger.info('Stream canceled');
|
|
119
|
-
sseServer.shutdown();
|
|
120
|
-
state.busy = false;
|
|
121
|
-
},
|
|
122
|
-
});
|
|
123
|
-
return new Response(stream, res);
|
|
124
91
|
});
|
|
125
92
|
});
|
|
126
93
|
return app;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type Agent } from '@looopy-ai/core';
|
|
2
|
+
import type pino from 'pino';
|
|
3
|
+
export declare const handlePrompt: <AuthContext>(agent: Agent<AuthContext>, body: unknown, authContext: AuthContext | undefined, res: Response, logger: pino.Logger, onComplete: () => void, onError: () => void) => Promise<Response>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { SSEServer } from '@looopy-ai/core';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
const promptValidator = z.looseObject({
|
|
4
|
+
prompt: z.string().min(1),
|
|
5
|
+
});
|
|
6
|
+
export const handlePrompt = async (agent, body, authContext, res, logger, onComplete, onError) => {
|
|
7
|
+
const promptValidation = promptValidator.safeParse(body);
|
|
8
|
+
if (!promptValidation.success) {
|
|
9
|
+
onError();
|
|
10
|
+
return Response.json({ error: 'Invalid prompt', details: promptValidation.error.issues }, { status: 400 });
|
|
11
|
+
}
|
|
12
|
+
const { prompt, ...metadata } = promptValidation.data;
|
|
13
|
+
const sseServer = new SSEServer();
|
|
14
|
+
const turn = await agent.startTurn(prompt, { authContext, metadata });
|
|
15
|
+
turn.subscribe({
|
|
16
|
+
next: (evt) => {
|
|
17
|
+
sseServer.emit(agent.contextId, evt);
|
|
18
|
+
},
|
|
19
|
+
complete: async () => {
|
|
20
|
+
sseServer.shutdown();
|
|
21
|
+
onComplete();
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
logger.info('SSE connection established');
|
|
25
|
+
const stream = new ReadableStream({
|
|
26
|
+
start(controller) {
|
|
27
|
+
sseServer.subscribe({
|
|
28
|
+
setHeader: (name, value) => {
|
|
29
|
+
res.headers.set(name, value);
|
|
30
|
+
},
|
|
31
|
+
write: (chunk) => {
|
|
32
|
+
controller.enqueue(new TextEncoder().encode(chunk));
|
|
33
|
+
},
|
|
34
|
+
end: function () {
|
|
35
|
+
logger.info('SSE stream finished');
|
|
36
|
+
this.writable = false;
|
|
37
|
+
controller.close();
|
|
38
|
+
},
|
|
39
|
+
}, { contextId: agent.contextId }, undefined);
|
|
40
|
+
},
|
|
41
|
+
cancel: () => {
|
|
42
|
+
logger.info('Stream canceled');
|
|
43
|
+
sseServer.shutdown();
|
|
44
|
+
onComplete();
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
return new Response(stream, res);
|
|
48
|
+
};
|
|
@@ -12,6 +12,7 @@ export interface DynamoDBAgentStoreConfig {
|
|
|
12
12
|
entityType?: string;
|
|
13
13
|
documentClient?: DynamoDBDocumentClient;
|
|
14
14
|
dynamoDbClientConfig?: DynamoDBClientConfig;
|
|
15
|
+
ttlSeconds?: number;
|
|
15
16
|
}
|
|
16
17
|
export declare class DynamoDBAgentStore implements AgentStore {
|
|
17
18
|
private readonly tableName;
|
|
@@ -23,6 +24,7 @@ export declare class DynamoDBAgentStore implements AgentStore {
|
|
|
23
24
|
private readonly consistentRead;
|
|
24
25
|
private readonly entityType;
|
|
25
26
|
private readonly documentClient;
|
|
27
|
+
private readonly ttlSeconds;
|
|
26
28
|
constructor(config: DynamoDBAgentStoreConfig);
|
|
27
29
|
load(contextId: string): Promise<AgentState | null>;
|
|
28
30
|
save(contextId: string, state: AgentState): Promise<void>;
|
|
@@ -10,6 +10,7 @@ export class DynamoDBAgentStore {
|
|
|
10
10
|
consistentRead;
|
|
11
11
|
entityType;
|
|
12
12
|
documentClient;
|
|
13
|
+
ttlSeconds;
|
|
13
14
|
constructor(config) {
|
|
14
15
|
if (!config.tableName) {
|
|
15
16
|
throw new Error('DynamoDBAgentStore requires a tableName');
|
|
@@ -46,6 +47,7 @@ export class DynamoDBAgentStore {
|
|
|
46
47
|
return this.deserializeState(item.state);
|
|
47
48
|
}
|
|
48
49
|
async save(contextId, state) {
|
|
50
|
+
const now = Math.floor(Date.now() / 1000);
|
|
49
51
|
const serializable = this.serializeState(state);
|
|
50
52
|
const command = new PutCommand({
|
|
51
53
|
TableName: this.tableName,
|
|
@@ -53,7 +55,8 @@ export class DynamoDBAgentStore {
|
|
|
53
55
|
...this.buildKey(contextId),
|
|
54
56
|
entityType: this.entityType,
|
|
55
57
|
state: serializable,
|
|
56
|
-
updatedAt:
|
|
58
|
+
updatedAt: now,
|
|
59
|
+
expires: this.ttlSeconds ? now + this.ttlSeconds : null,
|
|
57
60
|
},
|
|
58
61
|
});
|
|
59
62
|
await this.documentClient.send(command);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@looopy-ai/aws",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.15",
|
|
4
4
|
"description": "AWS storage and providers for Looopy AI",
|
|
5
5
|
"repository": {
|
|
6
6
|
"url": "https://github.com/looopy-ai/lib"
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"@smithy/types": "^4.9.0",
|
|
56
56
|
"hono": "^4.10.5",
|
|
57
57
|
"pino-http": "^11.0.0",
|
|
58
|
-
"@looopy-ai/core": "2.1.
|
|
58
|
+
"@looopy-ai/core": "2.1.14"
|
|
59
59
|
},
|
|
60
60
|
"publishConfig": {
|
|
61
61
|
"access": "public"
|