Spaces:
Sleeping
Sleeping
wuyiqunLu
commited on
feat: vision agent streaming (#32)
Browse files<img width="1493" alt="image"
src="https://github.com/landing-ai/vision-agent-ui/assets/132986242/bcaf6c02-6076-41a0-a1b0-83197bc90af7">
Not able to test error logging at the moment, will add later
- app/api/vision-agent/route.ts +58 -4
- lib/logger.ts +27 -14
app/api/vision-agent/route.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
| 1 |
-
import {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
// import { auth } from '@/auth';
|
| 4 |
import { MessageBase } from '../../../lib/types';
|
| 5 |
-
import { withLogging } from '@/lib/logger';
|
| 6 |
import { CLEANED_SEPARATOR } from '@/lib/constants';
|
| 7 |
import { cleanAnswerMessage, cleanInputMessage } from '@/lib/messageUtils';
|
| 8 |
|
|
@@ -12,12 +16,13 @@ export const maxDuration = 300; // This function can run for a maximum of 5 minu
|
|
| 12 |
|
| 13 |
export const POST = withLogging(
|
| 14 |
async (
|
| 15 |
-
|
| 16 |
json: {
|
| 17 |
messages: MessageBase[];
|
| 18 |
id: string;
|
| 19 |
url: string;
|
| 20 |
},
|
|
|
|
| 21 |
) => {
|
| 22 |
const { messages, url } = json;
|
| 23 |
|
|
@@ -65,8 +70,57 @@ export const POST = withLogging(
|
|
| 65 |
},
|
| 66 |
);
|
| 67 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
if (fetchResponse.body) {
|
| 69 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
} else {
|
| 71 |
return fetchResponse;
|
| 72 |
}
|
|
|
|
| 1 |
+
import {
|
| 2 |
+
StreamingTextResponse,
|
| 3 |
+
experimental_StreamData,
|
| 4 |
+
createStreamDataTransformer,
|
| 5 |
+
} from 'ai';
|
| 6 |
|
| 7 |
// import { auth } from '@/auth';
|
| 8 |
import { MessageBase } from '../../../lib/types';
|
| 9 |
+
import { logger, withLogging } from '@/lib/logger';
|
| 10 |
import { CLEANED_SEPARATOR } from '@/lib/constants';
|
| 11 |
import { cleanAnswerMessage, cleanInputMessage } from '@/lib/messageUtils';
|
| 12 |
|
|
|
|
| 16 |
|
| 17 |
export const POST = withLogging(
|
| 18 |
async (
|
| 19 |
+
session,
|
| 20 |
json: {
|
| 21 |
messages: MessageBase[];
|
| 22 |
id: string;
|
| 23 |
url: string;
|
| 24 |
},
|
| 25 |
+
request,
|
| 26 |
) => {
|
| 27 |
const { messages, url } = json;
|
| 28 |
|
|
|
|
| 70 |
},
|
| 71 |
);
|
| 72 |
|
| 73 |
+
if (!fetchResponse.ok) {
|
| 74 |
+
if (fetchResponse.body) {
|
| 75 |
+
const reader = fetchResponse.body.getReader();
|
| 76 |
+
return new StreamingTextResponse(
|
| 77 |
+
new ReadableStream({
|
| 78 |
+
async start(controller) {
|
| 79 |
+
const { done, value } = await reader.read();
|
| 80 |
+
if (!done) {
|
| 81 |
+
const errorText = new TextDecoder().decode(value);
|
| 82 |
+
logger.error(session, errorText, request);
|
| 83 |
+
controller.error(new Error(`Response error: ${errorText}`));
|
| 84 |
+
}
|
| 85 |
+
},
|
| 86 |
+
}),
|
| 87 |
+
{
|
| 88 |
+
status: 400,
|
| 89 |
+
},
|
| 90 |
+
);
|
| 91 |
+
} else {
|
| 92 |
+
return new StreamingTextResponse(
|
| 93 |
+
new ReadableStream({
|
| 94 |
+
start(controller) {
|
| 95 |
+
logger.error(
|
| 96 |
+
session,
|
| 97 |
+
'Response error: No response body',
|
| 98 |
+
request,
|
| 99 |
+
);
|
| 100 |
+
controller.error(new Error('Response error: No response body'));
|
| 101 |
+
},
|
| 102 |
+
}),
|
| 103 |
+
{
|
| 104 |
+
status: 400,
|
| 105 |
+
},
|
| 106 |
+
);
|
| 107 |
+
}
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
if (fetchResponse.body) {
|
| 111 |
+
const encoder = new TextEncoder();
|
| 112 |
+
const decoder = new TextDecoder();
|
| 113 |
+
|
| 114 |
+
const stream = fetchResponse.body.pipeThrough(
|
| 115 |
+
new TransformStream({
|
| 116 |
+
transform: async (chunk, controller) => {
|
| 117 |
+
const message = decoder.decode(chunk);
|
| 118 |
+
logger.info(session, { message }, request, '__AGENT_RESPONSE');
|
| 119 |
+
controller.enqueue(encoder.encode(message));
|
| 120 |
+
},
|
| 121 |
+
}),
|
| 122 |
+
);
|
| 123 |
+
return new StreamingTextResponse(stream);
|
| 124 |
} else {
|
| 125 |
return fetchResponse;
|
| 126 |
}
|
lib/logger.ts
CHANGED
|
@@ -38,16 +38,20 @@ class Logger {
|
|
| 38 |
}
|
| 39 |
|
| 40 |
private createLogMessage(
|
| 41 |
-
request: Request,
|
| 42 |
message: Object | string,
|
| 43 |
session: Session | null,
|
|
|
|
| 44 |
logDescription?: string,
|
| 45 |
) {
|
| 46 |
const body = {
|
| 47 |
type: logDescription || '__DEFAULT',
|
| 48 |
context: {
|
| 49 |
-
|
| 50 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
sessionUserId: session?.user.id,
|
| 52 |
sessionUserName: session?.user.name,
|
| 53 |
sessionUserEmail: session?.user.email,
|
|
@@ -58,62 +62,71 @@ class Logger {
|
|
| 58 |
}
|
| 59 |
|
| 60 |
info(
|
| 61 |
-
request: Request,
|
| 62 |
session: Session | null,
|
| 63 |
message: string | Object,
|
|
|
|
| 64 |
logDescription?: string,
|
| 65 |
) {
|
| 66 |
this.lokiLogger.info(
|
| 67 |
-
this.createLogMessage(
|
| 68 |
);
|
| 69 |
}
|
| 70 |
warn(
|
| 71 |
-
request: Request,
|
| 72 |
session: Session | null,
|
| 73 |
message: string | Object,
|
|
|
|
| 74 |
logDescription?: string,
|
| 75 |
) {
|
| 76 |
this.lokiLogger.warn(
|
| 77 |
-
this.createLogMessage(
|
| 78 |
);
|
| 79 |
}
|
| 80 |
debug(
|
| 81 |
-
request: Request,
|
| 82 |
session: Session | null,
|
| 83 |
message: string | Object,
|
|
|
|
| 84 |
logDescription?: string,
|
| 85 |
) {
|
| 86 |
this.lokiLogger.debug(
|
| 87 |
-
this.createLogMessage(
|
| 88 |
);
|
| 89 |
}
|
| 90 |
error(
|
| 91 |
-
request: Request,
|
| 92 |
session: Session | null,
|
| 93 |
message: string | Object,
|
|
|
|
| 94 |
logDescription?: string,
|
| 95 |
) {
|
| 96 |
this.lokiLogger.error(
|
| 97 |
-
this.createLogMessage(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
);
|
| 99 |
}
|
| 100 |
}
|
| 101 |
|
| 102 |
export const withLogging = (
|
| 103 |
-
handler: (
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
) => {
|
| 105 |
return async (req: Request) => {
|
| 106 |
const session = await auth();
|
| 107 |
const json = await req.json();
|
| 108 |
logger.info(
|
| 109 |
-
req,
|
| 110 |
session,
|
| 111 |
{
|
| 112 |
params: json,
|
| 113 |
},
|
|
|
|
| 114 |
'_API_REQUEST',
|
| 115 |
);
|
| 116 |
-
return handler(session, json);
|
| 117 |
};
|
| 118 |
};
|
| 119 |
|
|
|
|
| 38 |
}
|
| 39 |
|
| 40 |
private createLogMessage(
|
|
|
|
| 41 |
message: Object | string,
|
| 42 |
session: Session | null,
|
| 43 |
+
request?: Request,
|
| 44 |
logDescription?: string,
|
| 45 |
) {
|
| 46 |
const body = {
|
| 47 |
type: logDescription || '__DEFAULT',
|
| 48 |
context: {
|
| 49 |
+
...(request
|
| 50 |
+
? {
|
| 51 |
+
method: request?.method,
|
| 52 |
+
url: request.url,
|
| 53 |
+
}
|
| 54 |
+
: {}),
|
| 55 |
sessionUserId: session?.user.id,
|
| 56 |
sessionUserName: session?.user.name,
|
| 57 |
sessionUserEmail: session?.user.email,
|
|
|
|
| 62 |
}
|
| 63 |
|
| 64 |
info(
|
|
|
|
| 65 |
session: Session | null,
|
| 66 |
message: string | Object,
|
| 67 |
+
request?: Request,
|
| 68 |
logDescription?: string,
|
| 69 |
) {
|
| 70 |
this.lokiLogger.info(
|
| 71 |
+
this.createLogMessage(message, session, request, logDescription),
|
| 72 |
);
|
| 73 |
}
|
| 74 |
warn(
|
|
|
|
| 75 |
session: Session | null,
|
| 76 |
message: string | Object,
|
| 77 |
+
request?: Request,
|
| 78 |
logDescription?: string,
|
| 79 |
) {
|
| 80 |
this.lokiLogger.warn(
|
| 81 |
+
this.createLogMessage(message, session, request, logDescription),
|
| 82 |
);
|
| 83 |
}
|
| 84 |
debug(
|
|
|
|
| 85 |
session: Session | null,
|
| 86 |
message: string | Object,
|
| 87 |
+
request?: Request,
|
| 88 |
logDescription?: string,
|
| 89 |
) {
|
| 90 |
this.lokiLogger.debug(
|
| 91 |
+
this.createLogMessage(message, session, request, logDescription),
|
| 92 |
);
|
| 93 |
}
|
| 94 |
error(
|
|
|
|
| 95 |
session: Session | null,
|
| 96 |
message: string | Object,
|
| 97 |
+
request?: Request,
|
| 98 |
logDescription?: string,
|
| 99 |
) {
|
| 100 |
this.lokiLogger.error(
|
| 101 |
+
this.createLogMessage(
|
| 102 |
+
message,
|
| 103 |
+
session,
|
| 104 |
+
request,
|
| 105 |
+
logDescription ?? '__ERROR',
|
| 106 |
+
),
|
| 107 |
);
|
| 108 |
}
|
| 109 |
}
|
| 110 |
|
| 111 |
export const withLogging = (
|
| 112 |
+
handler: (
|
| 113 |
+
session: Session | null,
|
| 114 |
+
json: any,
|
| 115 |
+
request: Request,
|
| 116 |
+
) => Promise<Response>,
|
| 117 |
) => {
|
| 118 |
return async (req: Request) => {
|
| 119 |
const session = await auth();
|
| 120 |
const json = await req.json();
|
| 121 |
logger.info(
|
|
|
|
| 122 |
session,
|
| 123 |
{
|
| 124 |
params: json,
|
| 125 |
},
|
| 126 |
+
req,
|
| 127 |
'_API_REQUEST',
|
| 128 |
);
|
| 129 |
+
return handler(session, json, req);
|
| 130 |
};
|
| 131 |
};
|
| 132 |
|