wuyiqunLu commited on
Commit
139a0f4
1 Parent(s): 54a4eaa

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

Files changed (2) hide show
  1. app/api/vision-agent/route.ts +58 -4
  2. lib/logger.ts +27 -14
app/api/vision-agent/route.ts CHANGED
@@ -1,8 +1,12 @@
1
- import { StreamingTextResponse } from 'ai';
 
 
 
 
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
- _session,
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
- return new StreamingTextResponse(fetchResponse.body);
 
 
 
 
 
 
 
 
 
 
 
 
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
- method: request.method,
50
- url: request.url,
 
 
 
 
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(request, message, session, logDescription),
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(request, message, session, logDescription),
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(request, message, session, logDescription),
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(request, message, session, logDescription),
 
 
 
 
 
98
  );
99
  }
100
  }
101
 
102
  export const withLogging = (
103
- handler: (session: Session | null, json: any) => Promise<Response>,
 
 
 
 
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