langwatch 0.0.1
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/.eslintrc.cjs +37 -0
- package/README.md +3 -0
- package/dist/chunk-GOA2HL4A.mjs +269 -0
- package/dist/chunk-GOA2HL4A.mjs.map +1 -0
- package/dist/index.d.mts +82 -0
- package/dist/index.d.ts +82 -0
- package/dist/index.js +940 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +666 -0
- package/dist/index.mjs.map +1 -0
- package/dist/utils-s3gGR6vj.d.mts +209 -0
- package/dist/utils-s3gGR6vj.d.ts +209 -0
- package/dist/utils.d.mts +3 -0
- package/dist/utils.d.ts +3 -0
- package/dist/utils.js +263 -0
- package/dist/utils.js.map +1 -0
- package/dist/utils.mjs +7 -0
- package/dist/utils.mjs.map +1 -0
- package/example/.env.example +12 -0
- package/example/.eslintrc.json +26 -0
- package/example/LICENSE +13 -0
- package/example/README.md +10 -0
- package/example/app/(chat)/chat/[id]/page.tsx +60 -0
- package/example/app/(chat)/layout.tsx +14 -0
- package/example/app/(chat)/page.tsx +22 -0
- package/example/app/actions.ts +156 -0
- package/example/app/globals.css +76 -0
- package/example/app/layout.tsx +64 -0
- package/example/app/login/actions.ts +71 -0
- package/example/app/login/page.tsx +18 -0
- package/example/app/new/page.tsx +5 -0
- package/example/app/opengraph-image.png +0 -0
- package/example/app/share/[id]/page.tsx +58 -0
- package/example/app/signup/actions.ts +111 -0
- package/example/app/signup/page.tsx +18 -0
- package/example/app/twitter-image.png +0 -0
- package/example/auth.config.ts +42 -0
- package/example/auth.ts +45 -0
- package/example/components/button-scroll-to-bottom.tsx +36 -0
- package/example/components/chat-history.tsx +49 -0
- package/example/components/chat-list.tsx +52 -0
- package/example/components/chat-message-actions.tsx +40 -0
- package/example/components/chat-message.tsx +80 -0
- package/example/components/chat-panel.tsx +139 -0
- package/example/components/chat-share-dialog.tsx +95 -0
- package/example/components/chat.tsx +84 -0
- package/example/components/clear-history.tsx +75 -0
- package/example/components/empty-screen.tsx +38 -0
- package/example/components/external-link.tsx +29 -0
- package/example/components/footer.tsx +19 -0
- package/example/components/header.tsx +80 -0
- package/example/components/login-button.tsx +42 -0
- package/example/components/login-form.tsx +97 -0
- package/example/components/markdown.tsx +9 -0
- package/example/components/prompt-form.tsx +115 -0
- package/example/components/providers.tsx +17 -0
- package/example/components/sidebar-actions.tsx +125 -0
- package/example/components/sidebar-desktop.tsx +19 -0
- package/example/components/sidebar-footer.tsx +16 -0
- package/example/components/sidebar-item.tsx +124 -0
- package/example/components/sidebar-items.tsx +42 -0
- package/example/components/sidebar-list.tsx +38 -0
- package/example/components/sidebar-mobile.tsx +31 -0
- package/example/components/sidebar-toggle.tsx +24 -0
- package/example/components/sidebar.tsx +21 -0
- package/example/components/signup-form.tsx +95 -0
- package/example/components/stocks/events-skeleton.tsx +31 -0
- package/example/components/stocks/events.tsx +30 -0
- package/example/components/stocks/index.tsx +36 -0
- package/example/components/stocks/message.tsx +134 -0
- package/example/components/stocks/spinner.tsx +16 -0
- package/example/components/stocks/stock-purchase.tsx +146 -0
- package/example/components/stocks/stock-skeleton.tsx +22 -0
- package/example/components/stocks/stock.tsx +210 -0
- package/example/components/stocks/stocks-skeleton.tsx +9 -0
- package/example/components/stocks/stocks.tsx +67 -0
- package/example/components/tailwind-indicator.tsx +14 -0
- package/example/components/theme-toggle.tsx +31 -0
- package/example/components/ui/alert-dialog.tsx +141 -0
- package/example/components/ui/badge.tsx +36 -0
- package/example/components/ui/button.tsx +57 -0
- package/example/components/ui/codeblock.tsx +148 -0
- package/example/components/ui/dialog.tsx +122 -0
- package/example/components/ui/dropdown-menu.tsx +205 -0
- package/example/components/ui/icons.tsx +507 -0
- package/example/components/ui/input.tsx +25 -0
- package/example/components/ui/label.tsx +26 -0
- package/example/components/ui/select.tsx +164 -0
- package/example/components/ui/separator.tsx +31 -0
- package/example/components/ui/sheet.tsx +140 -0
- package/example/components/ui/sonner.tsx +31 -0
- package/example/components/ui/switch.tsx +29 -0
- package/example/components/ui/textarea.tsx +24 -0
- package/example/components/ui/tooltip.tsx +30 -0
- package/example/components/user-menu.tsx +53 -0
- package/example/components.json +17 -0
- package/example/lib/chat/actions.tsx +606 -0
- package/example/lib/hooks/use-copy-to-clipboard.tsx +33 -0
- package/example/lib/hooks/use-enter-submit.tsx +23 -0
- package/example/lib/hooks/use-local-storage.ts +24 -0
- package/example/lib/hooks/use-scroll-anchor.tsx +86 -0
- package/example/lib/hooks/use-sidebar.tsx +60 -0
- package/example/lib/hooks/use-streamable-text.ts +25 -0
- package/example/lib/types.ts +41 -0
- package/example/lib/utils.ts +89 -0
- package/example/middleware.ts +8 -0
- package/example/next-env.d.ts +5 -0
- package/example/next.config.js +13 -0
- package/example/package-lock.json +9249 -0
- package/example/package.json +77 -0
- package/example/pnpm-lock.yaml +5712 -0
- package/example/postcss.config.js +6 -0
- package/example/prettier.config.cjs +34 -0
- package/example/public/apple-touch-icon.png +0 -0
- package/example/public/favicon-16x16.png +0 -0
- package/example/public/favicon.ico +0 -0
- package/example/public/next.svg +1 -0
- package/example/public/thirteen.svg +1 -0
- package/example/public/vercel.svg +1 -0
- package/example/tailwind.config.ts +81 -0
- package/example/tsconfig.json +35 -0
- package/package.json +45 -0
- package/src/helpers.ts +64 -0
- package/src/index.test.ts +255 -0
- package/src/index.ts +397 -0
- package/src/server/types/.gitkeep +0 -0
- package/src/types.ts +69 -0
- package/src/utils.ts +134 -0
- package/ts-to-zod.config.js +18 -0
- package/tsconfig.json +32 -0
- package/tsup.config.ts +10 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** @type {import('prettier').Config} */
|
|
2
|
+
module.exports = {
|
|
3
|
+
endOfLine: 'lf',
|
|
4
|
+
semi: false,
|
|
5
|
+
useTabs: false,
|
|
6
|
+
singleQuote: true,
|
|
7
|
+
arrowParens: 'avoid',
|
|
8
|
+
tabWidth: 2,
|
|
9
|
+
trailingComma: 'none',
|
|
10
|
+
importOrder: [
|
|
11
|
+
'^(react/(.*)$)|^(react$)',
|
|
12
|
+
'^(next/(.*)$)|^(next$)',
|
|
13
|
+
'<THIRD_PARTY_MODULES>',
|
|
14
|
+
'',
|
|
15
|
+
'^types$',
|
|
16
|
+
'^@/types/(.*)$',
|
|
17
|
+
'^@/config/(.*)$',
|
|
18
|
+
'^@/lib/(.*)$',
|
|
19
|
+
'^@/hooks/(.*)$',
|
|
20
|
+
'^@/components/ui/(.*)$',
|
|
21
|
+
'^@/components/(.*)$',
|
|
22
|
+
'^@/registry/(.*)$',
|
|
23
|
+
'^@/styles/(.*)$',
|
|
24
|
+
'^@/app/(.*)$',
|
|
25
|
+
'',
|
|
26
|
+
'^[./]'
|
|
27
|
+
],
|
|
28
|
+
importOrderSeparation: false,
|
|
29
|
+
importOrderSortSpecifiers: true,
|
|
30
|
+
importOrderBuiltinModulesToTop: true,
|
|
31
|
+
importOrderParserPlugins: ['typescript', 'jsx', 'decorators-legacy'],
|
|
32
|
+
importOrderMergeDuplicateImports: true,
|
|
33
|
+
importOrderCombineTypeAndValueImports: true
|
|
34
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="31" fill="none"><g opacity=".9"><path fill="url(#a)" d="M13 .4v29.3H7V6.3h-.2L0 10.5V5L7.2.4H13Z"/><path fill="url(#b)" d="M28.8 30.1c-2.2 0-4-.3-5.7-1-1.7-.8-3-1.8-4-3.1a7.7 7.7 0 0 1-1.4-4.6h6.2c0 .8.3 1.4.7 2 .4.5 1 .9 1.7 1.2.7.3 1.6.4 2.5.4 1 0 1.7-.2 2.5-.5.7-.3 1.3-.8 1.7-1.4.4-.6.6-1.2.6-2s-.2-1.5-.7-2.1c-.4-.6-1-1-1.8-1.4-.8-.4-1.8-.5-2.9-.5h-2.7v-4.6h2.7a6 6 0 0 0 2.5-.5 4 4 0 0 0 1.7-1.3c.4-.6.6-1.3.6-2a3.5 3.5 0 0 0-2-3.3 5.6 5.6 0 0 0-4.5 0 4 4 0 0 0-1.7 1.2c-.4.6-.6 1.2-.6 2h-6c0-1.7.6-3.2 1.5-4.5 1-1.3 2.2-2.3 3.8-3C25 .4 26.8 0 28.8 0s3.8.4 5.3 1.1c1.5.7 2.7 1.7 3.6 3a7.2 7.2 0 0 1 1.2 4.2c0 1.6-.5 3-1.5 4a7 7 0 0 1-4 2.2v.2c2.2.3 3.8 1 5 2.2a6.4 6.4 0 0 1 1.6 4.6c0 1.7-.5 3.1-1.4 4.4a9.7 9.7 0 0 1-4 3.1c-1.7.8-3.7 1.1-5.8 1.1Z"/></g><defs><linearGradient id="a" x1="20" x2="20" y1="0" y2="30.1" gradientUnits="userSpaceOnUse"><stop/><stop offset="1" stop-color="#3D3D3D"/></linearGradient><linearGradient id="b" x1="20" x2="20" y1="0" y2="30.1" gradientUnits="userSpaceOnUse"><stop/><stop offset="1" stop-color="#3D3D3D"/></linearGradient></defs></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/** @type {import('tailwindcss').Config} */
|
|
2
|
+
module.exports = {
|
|
3
|
+
darkMode: ['class'],
|
|
4
|
+
content: [
|
|
5
|
+
'./pages/**/*.{ts,tsx}',
|
|
6
|
+
'./components/**/*.{ts,tsx}',
|
|
7
|
+
'./app/**/*.{ts,tsx}',
|
|
8
|
+
'./src/**/*.{ts,tsx}'
|
|
9
|
+
],
|
|
10
|
+
prefix: '',
|
|
11
|
+
theme: {
|
|
12
|
+
container: {
|
|
13
|
+
center: true,
|
|
14
|
+
padding: '2rem',
|
|
15
|
+
screens: {
|
|
16
|
+
'2xl': '1400px'
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
extend: {
|
|
20
|
+
fontFamily: {
|
|
21
|
+
sans: ['var(--font-geist-sans)'],
|
|
22
|
+
mono: ['var(--font-geist-mono)']
|
|
23
|
+
},
|
|
24
|
+
colors: {
|
|
25
|
+
border: 'hsl(var(--border))',
|
|
26
|
+
input: 'hsl(var(--input))',
|
|
27
|
+
ring: 'hsl(var(--ring))',
|
|
28
|
+
background: 'hsl(var(--background))',
|
|
29
|
+
foreground: 'hsl(var(--foreground))',
|
|
30
|
+
primary: {
|
|
31
|
+
DEFAULT: 'hsl(var(--primary))',
|
|
32
|
+
foreground: 'hsl(var(--primary-foreground))'
|
|
33
|
+
},
|
|
34
|
+
secondary: {
|
|
35
|
+
DEFAULT: 'hsl(var(--secondary))',
|
|
36
|
+
foreground: 'hsl(var(--secondary-foreground))'
|
|
37
|
+
},
|
|
38
|
+
destructive: {
|
|
39
|
+
DEFAULT: 'hsl(var(--destructive))',
|
|
40
|
+
foreground: 'hsl(var(--destructive-foreground))'
|
|
41
|
+
},
|
|
42
|
+
muted: {
|
|
43
|
+
DEFAULT: 'hsl(var(--muted))',
|
|
44
|
+
foreground: 'hsl(var(--muted-foreground))'
|
|
45
|
+
},
|
|
46
|
+
accent: {
|
|
47
|
+
DEFAULT: 'hsl(var(--accent))',
|
|
48
|
+
foreground: 'hsl(var(--accent-foreground))'
|
|
49
|
+
},
|
|
50
|
+
popover: {
|
|
51
|
+
DEFAULT: 'hsl(var(--popover))',
|
|
52
|
+
foreground: 'hsl(var(--popover-foreground))'
|
|
53
|
+
},
|
|
54
|
+
card: {
|
|
55
|
+
DEFAULT: 'hsl(var(--card))',
|
|
56
|
+
foreground: 'hsl(var(--card-foreground))'
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
borderRadius: {
|
|
60
|
+
lg: 'var(--radius)',
|
|
61
|
+
md: 'calc(var(--radius) - 2px)',
|
|
62
|
+
sm: 'calc(var(--radius) - 4px)'
|
|
63
|
+
},
|
|
64
|
+
keyframes: {
|
|
65
|
+
'accordion-down': {
|
|
66
|
+
from: { height: '0' },
|
|
67
|
+
to: { height: 'var(--radix-accordion-content-height)' }
|
|
68
|
+
},
|
|
69
|
+
'accordion-up': {
|
|
70
|
+
from: { height: 'var(--radix-accordion-content-height)' },
|
|
71
|
+
to: { height: '0' }
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
animation: {
|
|
75
|
+
'accordion-down': 'accordion-down 0.2s ease-out',
|
|
76
|
+
'accordion-up': 'accordion-up 0.2s ease-out'
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
plugins: [require('tailwindcss-animate'), require('@tailwindcss/typography')]
|
|
81
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
4
|
+
"allowJs": true,
|
|
5
|
+
"skipLibCheck": true,
|
|
6
|
+
"strict": true,
|
|
7
|
+
"forceConsistentCasingInFileNames": true,
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"incremental": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"module": "esnext",
|
|
12
|
+
"moduleResolution": "node",
|
|
13
|
+
"resolveJsonModule": true,
|
|
14
|
+
"isolatedModules": true,
|
|
15
|
+
"jsx": "preserve",
|
|
16
|
+
"baseUrl": ".",
|
|
17
|
+
"paths": {
|
|
18
|
+
"@/*": ["./*"]
|
|
19
|
+
},
|
|
20
|
+
"plugins": [
|
|
21
|
+
{
|
|
22
|
+
"name": "next"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"strictNullChecks": true
|
|
26
|
+
},
|
|
27
|
+
"include": [
|
|
28
|
+
"next-env.d.ts",
|
|
29
|
+
"next-auth.d.ts",
|
|
30
|
+
"**/*.ts",
|
|
31
|
+
"**/*.tsx",
|
|
32
|
+
".next/types/**/*.ts"
|
|
33
|
+
],
|
|
34
|
+
"exclude": ["node_modules"]
|
|
35
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "langwatch",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "dist/src/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/langwatch/langwatch.git",
|
|
11
|
+
"directory": "typescript-sdk"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"postinstall": "cp ../langwatch/src/server/tracer/types.ts src/server/types/tracer.ts && ts-to-zod src/server/types/tracer.ts src/server/types/tracer.generated.ts",
|
|
15
|
+
"test": "vitest",
|
|
16
|
+
"build": "tsup",
|
|
17
|
+
"prepublish": "npm run build"
|
|
18
|
+
},
|
|
19
|
+
"author": "",
|
|
20
|
+
"license": "Apache-2.0",
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=18.0.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@ai-sdk/openai": "^0.0.18",
|
|
26
|
+
"@eslint/js": "^9.4.0",
|
|
27
|
+
"@types/debug": "^4.1.12",
|
|
28
|
+
"@types/eslint__js": "^8.42.3",
|
|
29
|
+
"@types/node": "^16.0.0",
|
|
30
|
+
"eslint": "^8.57.0",
|
|
31
|
+
"ts-to-zod": "^3.4.1",
|
|
32
|
+
"tsup": "^8.1.0",
|
|
33
|
+
"typescript": "^4.9.5",
|
|
34
|
+
"typescript-eslint": "^7.11.0",
|
|
35
|
+
"vitest": "^0.5.0"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"ai": "^3.1.23",
|
|
39
|
+
"llm-cost": "^1.0.4",
|
|
40
|
+
"nanoid": "^5.0.7",
|
|
41
|
+
"openai": "^4.47.3",
|
|
42
|
+
"zod": "^3.22.4",
|
|
43
|
+
"zod-validation-error": "^3.3.0"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/src/helpers.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export type Strict<T> = T & { [K in Exclude<keyof any, keyof T>]: never };
|
|
2
|
+
|
|
3
|
+
type SnakeToCamelCase<S extends string> = S extends `${infer T}_${infer U}`
|
|
4
|
+
? `${T}${Capitalize<SnakeToCamelCase<U>>}`
|
|
5
|
+
: S;
|
|
6
|
+
|
|
7
|
+
export type SnakeToCamelCaseNested<T> = T extends object
|
|
8
|
+
? T extends (infer U)[]
|
|
9
|
+
? U extends object
|
|
10
|
+
? {
|
|
11
|
+
[K in keyof U as SnakeToCamelCase<
|
|
12
|
+
K & string
|
|
13
|
+
>]: SnakeToCamelCaseNested<U[K]>;
|
|
14
|
+
}[]
|
|
15
|
+
: T
|
|
16
|
+
: {
|
|
17
|
+
[K in keyof T as SnakeToCamelCase<K & string>]: SnakeToCamelCaseNested<
|
|
18
|
+
T[K]
|
|
19
|
+
>;
|
|
20
|
+
}
|
|
21
|
+
: T;
|
|
22
|
+
|
|
23
|
+
type CamelToSnakeCase<S extends string> = S extends `${infer T}${infer U}`
|
|
24
|
+
? `${T extends Capitalize<T> ? "_" : ""}${Lowercase<T>}${CamelToSnakeCase<U>}`
|
|
25
|
+
: S;
|
|
26
|
+
|
|
27
|
+
export type CamelToSnakeCaseNested<T> = T extends object
|
|
28
|
+
? T extends (infer U)[]
|
|
29
|
+
? U extends object
|
|
30
|
+
? {
|
|
31
|
+
[K in keyof U as CamelToSnakeCase<
|
|
32
|
+
K & string
|
|
33
|
+
>]: CamelToSnakeCaseNested<U[K]>;
|
|
34
|
+
}[]
|
|
35
|
+
: T
|
|
36
|
+
: {
|
|
37
|
+
[K in keyof T as CamelToSnakeCase<K & string>]: CamelToSnakeCaseNested<
|
|
38
|
+
T[K]
|
|
39
|
+
>;
|
|
40
|
+
}
|
|
41
|
+
: T;
|
|
42
|
+
|
|
43
|
+
function camelToSnakeCase(str: string): string {
|
|
44
|
+
return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function camelToSnakeCaseNested<T>(obj: T): CamelToSnakeCaseNested<T> {
|
|
48
|
+
if (Array.isArray(obj)) {
|
|
49
|
+
return obj.map((item) =>
|
|
50
|
+
camelToSnakeCaseNested(item)
|
|
51
|
+
) as CamelToSnakeCaseNested<T>;
|
|
52
|
+
} else if (typeof obj === "object" && obj !== null) {
|
|
53
|
+
const newObj: any = {};
|
|
54
|
+
for (const key in obj) {
|
|
55
|
+
if (obj.hasOwnProperty(key)) {
|
|
56
|
+
const newKey = camelToSnakeCase(key);
|
|
57
|
+
newObj[newKey] = camelToSnakeCaseNested((obj as any)[key]);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return newObj as CamelToSnakeCaseNested<T>;
|
|
61
|
+
} else {
|
|
62
|
+
return obj as CamelToSnakeCaseNested<T>;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { OpenAI } from "openai";
|
|
2
|
+
import { LangWatch, convertFromVercelAIMessages } from "./index";
|
|
3
|
+
import {
|
|
4
|
+
describe,
|
|
5
|
+
it,
|
|
6
|
+
expect,
|
|
7
|
+
vi,
|
|
8
|
+
beforeEach,
|
|
9
|
+
type SpyInstanceFn,
|
|
10
|
+
} from "vitest";
|
|
11
|
+
import { openai } from "@ai-sdk/openai";
|
|
12
|
+
import { generateText, type CoreMessage } from "ai";
|
|
13
|
+
|
|
14
|
+
describe("LangWatch tracer", () => {
|
|
15
|
+
let mockFetch: SpyInstanceFn;
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
const originalFetch = global.fetch;
|
|
19
|
+
// Mocking fetch to test the right data was sent to the server
|
|
20
|
+
mockFetch = vi.fn((url, ...args) => {
|
|
21
|
+
if (url.includes("localhost.test")) {
|
|
22
|
+
return Promise.resolve({
|
|
23
|
+
ok: true,
|
|
24
|
+
status: 200,
|
|
25
|
+
json: () => Promise.resolve({ message: "Trace sent" }),
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return originalFetch(url, ...args);
|
|
29
|
+
});
|
|
30
|
+
// @ts-ignore
|
|
31
|
+
global.fetch = mockFetch;
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("captures traces correctly", async () => {
|
|
35
|
+
const langwatch = new LangWatch({
|
|
36
|
+
apiKey: "test",
|
|
37
|
+
endpoint: "http://localhost.test",
|
|
38
|
+
});
|
|
39
|
+
const trace = langwatch.getTrace();
|
|
40
|
+
trace.update({
|
|
41
|
+
metadata: { threadId: "123", userId: "123", labels: ["foo"] },
|
|
42
|
+
});
|
|
43
|
+
trace.update({ metadata: { userId: "456", labels: ["bar"] } });
|
|
44
|
+
|
|
45
|
+
const span = trace.startSpan({
|
|
46
|
+
name: "weather_function",
|
|
47
|
+
input: {
|
|
48
|
+
type: "json",
|
|
49
|
+
value: {
|
|
50
|
+
city: "Tokyo",
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
span.end({
|
|
55
|
+
outputs: [
|
|
56
|
+
{
|
|
57
|
+
type: "json",
|
|
58
|
+
value: {
|
|
59
|
+
weather: "sunny",
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
expect(trace.metadata).toEqual({
|
|
66
|
+
threadId: "123",
|
|
67
|
+
userId: "456",
|
|
68
|
+
labels: ["foo", "bar"],
|
|
69
|
+
});
|
|
70
|
+
expect(span.timestamps.startedAt).toBeDefined();
|
|
71
|
+
expect(span.timestamps.finishedAt).toBeDefined();
|
|
72
|
+
|
|
73
|
+
const ragSpan = trace.startRAGSpan({
|
|
74
|
+
name: "my-vectordb-retrieval",
|
|
75
|
+
input: { type: "text", value: "search query" },
|
|
76
|
+
});
|
|
77
|
+
ragSpan.end({
|
|
78
|
+
contexts: [
|
|
79
|
+
{
|
|
80
|
+
documentId: "doc1",
|
|
81
|
+
content: "document chunk 1",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
documentId: "doc2",
|
|
85
|
+
content: "document chunk 2",
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const llmSpan = ragSpan.startLLMSpan({
|
|
91
|
+
name: "llm",
|
|
92
|
+
model: "gpt-3.5-turbo",
|
|
93
|
+
input: {
|
|
94
|
+
type: "chat_messages",
|
|
95
|
+
value: [
|
|
96
|
+
{ role: "system", content: "You are a helpful assistant." },
|
|
97
|
+
{ role: "user", content: "What is the weather in Tokyo?" },
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
llmSpan.end({
|
|
102
|
+
outputs: [
|
|
103
|
+
{
|
|
104
|
+
type: "chat_messages",
|
|
105
|
+
value: [
|
|
106
|
+
{
|
|
107
|
+
role: "assistant",
|
|
108
|
+
content: "It's cloudy in Tokyo.",
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
ragSpan.end();
|
|
116
|
+
|
|
117
|
+
await trace.sendSpans();
|
|
118
|
+
|
|
119
|
+
expect(mockFetch).toHaveBeenCalled();
|
|
120
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
121
|
+
"http://localhost.test/api/collector",
|
|
122
|
+
expect.objectContaining({
|
|
123
|
+
method: "POST",
|
|
124
|
+
headers: {
|
|
125
|
+
"X-Auth-Token": "test",
|
|
126
|
+
"Content-Type": "application/json",
|
|
127
|
+
},
|
|
128
|
+
body: expect.any(String),
|
|
129
|
+
})
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const firstCall: any = mockFetch.mock.calls[0];
|
|
133
|
+
const requestBody = JSON.parse(firstCall[1].body);
|
|
134
|
+
expect(requestBody.trace_id).toBeDefined();
|
|
135
|
+
expect(requestBody.metadata).toEqual({
|
|
136
|
+
thread_id: "123",
|
|
137
|
+
user_id: "456",
|
|
138
|
+
labels: ["foo", "bar"],
|
|
139
|
+
});
|
|
140
|
+
expect(requestBody.spans.length).toBe(3);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it.skip("captures openai llm call", async () => {
|
|
144
|
+
const langwatch = new LangWatch({
|
|
145
|
+
apiKey: "test",
|
|
146
|
+
endpoint: "http://localhost.test",
|
|
147
|
+
});
|
|
148
|
+
const trace = langwatch.getTrace();
|
|
149
|
+
|
|
150
|
+
// Model to be used and messages that will be sent to the LLM
|
|
151
|
+
const model = "gpt-4o";
|
|
152
|
+
const messages: OpenAI.Chat.ChatCompletionMessageParam[] = [
|
|
153
|
+
{ role: "system", content: "You are a helpful assistant." },
|
|
154
|
+
{
|
|
155
|
+
role: "user",
|
|
156
|
+
content: "Write a tweet-size vegetarian lasagna recipe for 4 people.",
|
|
157
|
+
},
|
|
158
|
+
];
|
|
159
|
+
|
|
160
|
+
// Capture the llm call with a span
|
|
161
|
+
const span = trace.startLLMSpan({
|
|
162
|
+
name: "llm",
|
|
163
|
+
model: model,
|
|
164
|
+
input: {
|
|
165
|
+
type: "chat_messages",
|
|
166
|
+
value: messages,
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// Continue with the LLM call normally
|
|
171
|
+
const openai = new OpenAI();
|
|
172
|
+
const chatCompletion = await openai.chat.completions.create({
|
|
173
|
+
messages: messages,
|
|
174
|
+
model: model,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
span.end({
|
|
178
|
+
outputs: chatCompletion.choices.map((choice) => ({
|
|
179
|
+
type: "chat_messages",
|
|
180
|
+
value: [choice.message],
|
|
181
|
+
})),
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
await trace.sendSpans();
|
|
185
|
+
|
|
186
|
+
expect(mockFetch).toHaveBeenCalled();
|
|
187
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
188
|
+
"http://localhost.test/api/collector",
|
|
189
|
+
expect.objectContaining({
|
|
190
|
+
method: "POST",
|
|
191
|
+
headers: {
|
|
192
|
+
"X-Auth-Token": "test",
|
|
193
|
+
"Content-Type": "application/json",
|
|
194
|
+
},
|
|
195
|
+
body: expect.any(String),
|
|
196
|
+
})
|
|
197
|
+
);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it.skip("captures vercel ai sdk call", async () => {
|
|
201
|
+
const langwatch = new LangWatch({
|
|
202
|
+
apiKey: "test",
|
|
203
|
+
endpoint: "http://localhost.test",
|
|
204
|
+
});
|
|
205
|
+
const trace = langwatch.getTrace();
|
|
206
|
+
|
|
207
|
+
// Model to be used and messages that will be sent to the LLM
|
|
208
|
+
const model = openai("gpt-4o");
|
|
209
|
+
const messages: CoreMessage[] = [
|
|
210
|
+
{ role: "system", content: "You are a helpful assistant." },
|
|
211
|
+
{
|
|
212
|
+
role: "user",
|
|
213
|
+
content: "Write a tweet-size vegetarian lasagna recipe for 4 people.",
|
|
214
|
+
},
|
|
215
|
+
];
|
|
216
|
+
|
|
217
|
+
const span = trace.startLLMSpan({
|
|
218
|
+
name: "llm",
|
|
219
|
+
model: model.modelId,
|
|
220
|
+
input: {
|
|
221
|
+
type: "chat_messages",
|
|
222
|
+
value: convertFromVercelAIMessages(messages),
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const response = await generateText({
|
|
227
|
+
model: model,
|
|
228
|
+
messages: messages,
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
span.end({
|
|
232
|
+
outputs: [
|
|
233
|
+
{
|
|
234
|
+
type: "chat_messages",
|
|
235
|
+
value: convertFromVercelAIMessages(response.responseMessages),
|
|
236
|
+
},
|
|
237
|
+
],
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
await trace.sendSpans();
|
|
241
|
+
|
|
242
|
+
expect(mockFetch).toHaveBeenCalled();
|
|
243
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
244
|
+
"http://localhost.test/api/collector",
|
|
245
|
+
expect.objectContaining({
|
|
246
|
+
method: "POST",
|
|
247
|
+
headers: {
|
|
248
|
+
"X-Auth-Token": "test",
|
|
249
|
+
"Content-Type": "application/json",
|
|
250
|
+
},
|
|
251
|
+
body: expect.any(String),
|
|
252
|
+
})
|
|
253
|
+
);
|
|
254
|
+
});
|
|
255
|
+
});
|