Skip to content

Commit

Permalink
Move typescript SKD to use v2 of API
Browse files Browse the repository at this point in the history
API v2 is the new version and from now on our typescript sdk will use
it. This change is necessary to allow users call the gateway in a
non-streaming way which in most cases is what they need
  • Loading branch information
andresgutgon committed Oct 28, 2024
1 parent 3bf4389 commit 900bccc
Show file tree
Hide file tree
Showing 78 changed files with 4,752 additions and 2,732 deletions.
1 change: 1 addition & 0 deletions apps/gateway/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@hono/node-server": "^1.12.0",
"@hono/zod-validator": "^0.2.2",
"@latitude-data/compiler": "workspace:^",
"@latitude-data/constants": "workspace:^",
"@latitude-data/core": "workspace:^",
"@latitude-data/env": "workspace:^",
"@sentry/cli": "^2.37.0",
Expand Down
2 changes: 1 addition & 1 deletion apps/gateway/src/common/sentry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Sentry.init({
export const captureException = (error: Error) => {
if (env.NODE_ENV === 'production') {
Sentry.captureException(error)
} else {
} else if (env.NODE_ENV === 'development') {
console.error(error)
}
}
Expand Down
38 changes: 35 additions & 3 deletions apps/gateway/src/middlewares/errorHandler.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,33 @@
import { ApiErrorCodes } from '@latitude-data/constants/errors'
import {
LatitudeError,
UnprocessableEntityError,
} from '@latitude-data/core/lib/errors'
import { getUnknownError } from '@latitude-data/core/lib/getUnknownError'
import { ChainError } from '@latitude-data/core/services/chains/ChainErrors/index'
import { captureException } from '$/common/sentry'
import { HTTPException } from 'hono/http-exception'

import HttpStatusCodes from '../common/httpStatusCodes'

function unprocessableExtraParameters(error: UnprocessableEntityError) {
const isChainError = error instanceof ChainError
if (!isChainError) return { name: error.name, errorCode: error.name }

const base = { name: 'DocumentRunError', errorCode: error.errorCode }
const runError = error.runError

if (!runError) return base

return {
...base,
dbErrorRef: {
entityUuid: runError.errorableUuid,
entityType: runError.errorableType,
},
}
}

const errorHandlerMiddleware = (err: Error) => {
if (process.env.NODE_ENV !== 'test') {
const unknownError = getUnknownError(err)
Expand All @@ -19,13 +39,18 @@ const errorHandlerMiddleware = (err: Error) => {

if (err instanceof HTTPException) {
return Response.json(
{ message: err.message },
{
name: ApiErrorCodes.HTTPExeption,
errorCode: ApiErrorCodes.HTTPExeption,
message: err.message,
details: { cause: err.cause },
},
{ status: err.status, headers: err.res?.headers },
)
} else if (err instanceof UnprocessableEntityError) {
return Response.json(
{
name: err.name,
...unprocessableExtraParameters(err),
message: err.message,
details: err.details,
},
Expand All @@ -34,14 +59,21 @@ const errorHandlerMiddleware = (err: Error) => {
} else if (err instanceof LatitudeError) {
return Response.json(
{
name: err.name,
errorCode: err.name,
message: err.message,
details: err.details,
},
{ status: err.statusCode, headers: err.headers },
)
} else {
return Response.json(
{ message: err.message },
{
name: 'InternalServerError',
errorCode: ApiErrorCodes.InternalServerError,
message: err.message,
details: { cause: err.cause },
},
{ status: HttpStatusCodes.INTERNAL_SERVER_ERROR },
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {
ChainStepObjectResponse,
ChainStepTextResponse,
RunSyncAPIResponse,
} from '@latitude-data/core/browser'
import { LatitudeError } from '@latitude-data/core/lib/errors'
import { Result, TypedResult } from '@latitude-data/core/lib/Result'
import { captureException } from '$/common/sentry'

type DocumentResponse = ChainStepObjectResponse | ChainStepTextResponse
export function documentRunPresenter(
response: DocumentResponse,
): TypedResult<RunSyncAPIResponse, LatitudeError> {
const conversation = response.providerLog?.messages
const uuid = response.documentLogUuid
const errorMessage = !uuid
? 'Document Log uuid not found in response'
: !conversation
? 'Conversation messages not found in response'
: undefined

const error = errorMessage ? new LatitudeError(errorMessage) : undefined

if (error) {
captureException(error)
return Result.error(error)
}

const type = response.streamType
return Result.ok({
uuid: uuid!,
conversation: conversation!,
response: {
streamType: type,
usage: response.usage!,
text: response.text,
object: type === 'object' ? response.object : undefined,
toolCalls: type === 'text' ? response.toolCalls : [],
},
})
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { MessageRole } from '@latitude-data/compiler'
import { RunErrorCodes } from '@latitude-data/constants/errors'
import {
ChainEventTypes,
Commit,
LogSources,
Project,
ProviderLog,
StreamEventTypes,
Workspace,
} from '@latitude-data/core/browser'
Expand All @@ -13,7 +16,10 @@ import {
createProject,
helpers,
} from '@latitude-data/core/factories'
import { LatitudeError } from '@latitude-data/core/lib/errors'
import { Result } from '@latitude-data/core/lib/Result'
import { ChainError } from '@latitude-data/core/services/chains/ChainErrors/index'
import { ChainResponse } from '@latitude-data/core/services/chains/run'
import { mergeCommit } from '@latitude-data/core/services/commits/merge'
import { parseSSEvent } from '$/common/parseSSEEvent'
import app from '$/routes/app'
Expand All @@ -22,6 +28,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'

const mocks = vi.hoisted(() => ({
runDocumentAtCommit: vi.fn(),
captureExceptionMock: vi.fn(),
queues: {
defaultQueue: {
jobs: {
Expand All @@ -32,6 +39,15 @@ const mocks = vi.hoisted(() => ({
},
}))

vi.mock('$/common/sentry', async (importOriginal) => {
const original = (await importOriginal()) as typeof importOriginal

return {
...original,
captureException: mocks.captureExceptionMock,
}
})

vi.mock(
'@latitude-data/core/services/commits/runDocumentAtCommit',
async (importOriginal) => {
Expand Down Expand Up @@ -322,7 +338,7 @@ describe('POST /run', () => {
})
})

it('returns last response', async () => {
it('returns response', async () => {
const stream = new ReadableStream({
start(controller) {
controller.enqueue({
Expand All @@ -340,8 +356,25 @@ describe('POST /run', () => {
},
})

const response = new Promise((resolve) => {
resolve({ text: 'Hello', usage: {} })
const response = new Promise<ChainResponse<'object'>>((resolve) => {
resolve(
Result.ok({
streamType: 'object',
object: { something: { else: 'here' } },
text: 'Hello',
usage: { promptTokens: 4, completionTokens: 6, totalTokens: 10 },
documentLogUuid: 'fake-document-log-uuid',
providerLog: {
messages: [
{
role: MessageRole.assistant,
toolCalls: [],
content: 'Hello',
},
],
} as unknown as ProviderLog,
}),
)
})

mocks.runDocumentAtCommit.mockReturnValue(
Expand All @@ -363,12 +396,151 @@ describe('POST /run', () => {

expect(res.status).toBe(200)
expect(await res.json()).toEqual({
type: ChainEventTypes.Complete,
uuid: 'fake-document-log-uuid',
conversation: [
{
role: MessageRole.assistant,
toolCalls: [],
content: 'Hello',
},
],
response: {
streamType: 'object',
usage: { promptTokens: 4, completionTokens: 6, totalTokens: 10 },
text: 'Hello',
usage: {},
object: { something: { else: 'here' } },
toolCalls: [],
},
})
})

it('returns error when runDocumentAtCommit has an error', async () => {
const response = new Promise<ChainResponse<'object'>>((resolve) => {
resolve(
Result.error(
new ChainError({
code: RunErrorCodes.ChainCompileError,
message: 'Error compiling prompt for document uuid',
}),
),
)
})

mocks.runDocumentAtCommit.mockReturnValue(
new Promise((resolve) => {
resolve(
Result.ok({
stream: new ReadableStream(),
response,
}),
)
}),
)

const res = await app.request(route, {
method: 'POST',
body,
headers,
})

expect(res.status).toBe(422)
expect(await res.json()).toEqual({
name: 'DocumentRunError',
errorCode: RunErrorCodes.ChainCompileError,
message: 'Error compiling prompt for document uuid',
details: {
errorCode: RunErrorCodes.ChainCompileError,
},
})
})

it('returns error if runDocumentAtCommit has not documentLogUuid', async () => {
const response = new Promise<ChainResponse<'object'>>((resolve) => {
resolve(
Result.ok({
streamType: 'object',
object: { something: { else: 'here' } },
text: 'Hello',
usage: { promptTokens: 4, completionTokens: 6, totalTokens: 10 },
providerLog: {
messages: [
{
role: MessageRole.assistant,
toolCalls: [],
content: 'Hello',
},
],
} as unknown as ProviderLog,
}),
)
})

mocks.runDocumentAtCommit.mockReturnValue(
new Promise((resolve) => {
resolve(
Result.ok({
stream: new ReadableStream(),
response,
}),
)
}),
)

const res = await app.request(route, {
method: 'POST',
body,
headers,
})

expect(res.status).toBe(500)
expect(await res.json()).toEqual({
name: 'LatitudeError',
errorCode: 'LatitudeError',
message: 'Document Log uuid not found in response',
details: {},
})
expect(mocks.captureExceptionMock).toHaveBeenCalledWith(
new LatitudeError('Document Log uuid not found in response'),
)
})

it('returns error if runDocumentAtCommit has not providerLog', async () => {
const response = new Promise<ChainResponse<'object'>>((resolve) => {
resolve(
Result.ok({
streamType: 'object',
object: { something: { else: 'here' } },
text: 'Hello',
usage: { promptTokens: 4, completionTokens: 6, totalTokens: 10 },
documentLogUuid: 'fake-document-log-uuid',
}),
)
})

mocks.runDocumentAtCommit.mockReturnValue(
new Promise((resolve) => {
resolve(
Result.ok({
stream: new ReadableStream(),
response,
}),
)
}),
)

const res = await app.request(route, {
method: 'POST',
body,
headers,
})

expect(res.status).toBe(500)
expect(await res.json()).toEqual({
name: 'LatitudeError',
errorCode: 'LatitudeError',
message: 'Conversation messages not found in response',
details: {},
})
})
})
})
Loading

0 comments on commit 900bccc

Please sign in to comment.