diff --git a/package.json b/package.json index dc96d19ee3cb..174eeb136b49 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,6 @@ }, "dependencies": { "@openctx/client": "^0.0.30", - "@opentelemetry/sdk-trace-web": "^1.18.1", "@sourcegraph/telemetry": "^0.18.0", "observable-fns": "^0.6.1", "react": "18.2.0", @@ -79,7 +78,6 @@ }, "pnpm": { "overrides": { - "tslib": "2.1.0", "@lexical/react": "https://storage.googleapis.com/sourcegraph-assets/npm/lexical-react-sourcegraph-fork-31065486.tgz" }, "packageExtensions": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 872504903db2..8ce96ae0d950 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,7 +9,6 @@ neverBuiltDependencies: - playwright overrides: - tslib: 2.1.0 '@lexical/react': https://storage.googleapis.com/sourcegraph-assets/npm/lexical-react-sourcegraph-fork-31065486.tgz packageExtensionsChecksum: bc809394d179d8460f9d473fc54e379a @@ -26,9 +25,6 @@ importers: '@openctx/client': specifier: ^0.0.30 version: 0.0.30 - '@opentelemetry/sdk-trace-web': - specifier: ^1.18.1 - version: 1.18.1(@opentelemetry/api@1.7.0) '@sourcegraph/telemetry': specifier: ^0.18.0 version: 0.18.0 @@ -477,13 +473,13 @@ importers: version: 1.18.1(@opentelemetry/api@1.7.0) '@opentelemetry/sdk-trace-base': specifier: ^1.18.1 - version: 1.27.0(@opentelemetry/api@1.7.0) + version: 1.18.1(@opentelemetry/api@1.7.0) '@opentelemetry/sdk-trace-node': specifier: ^1.18.1 - version: 1.27.0(@opentelemetry/api@1.7.0) + version: 1.18.1(@opentelemetry/api@1.7.0) '@opentelemetry/semantic-conventions': specifier: ^1.18.1 - version: 1.27.0 + version: 1.18.1 '@radix-ui/react-accordion': specifier: ^1.2.0 version: 1.2.0(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0)(react@18.2.0) @@ -3482,7 +3478,7 @@ packages: '@microsoft/fast-element': 1.13.0 '@microsoft/fast-web-utilities': 5.4.1 tabbable: 5.3.3 - tslib: 2.1.0 + tslib: 1.14.1 dev: false /@microsoft/fast-react-wrapper@0.1.48(react@18.2.0): @@ -3646,11 +3642,11 @@ packages: resolution: {integrity: sha512-AdY5wvN0P2vXBi3b29hxZgSFvdhdxPB9+f0B6s//P9Q8nibRWeA3cHm8UmLpio9ABigkVHJ5NMPk+Mz8VCCyrw==} engines: {node: '>=8.0.0'} - /@opentelemetry/context-async-hooks@1.27.0(@opentelemetry/api@1.7.0): - resolution: {integrity: sha512-CdZ3qmHCwNhFAzjTgHqrDQ44Qxcpz43cVxZRhOs+Ns/79ug+Mr84Bkb626bkJLkA3+BLimA5YAEVRlJC6pFb7g==} + /@opentelemetry/context-async-hooks@1.18.1(@opentelemetry/api@1.7.0): + resolution: {integrity: sha512-HHfJR32NH2x0b69CACCwH8m1dpNALoCTtpgmIWMNkeMGNUeKT48d4AX4xsF4uIRuUoRTbTgtSBRvS+cF97qwCQ==} engines: {node: '>=14'} peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/api': '>=1.0.0 <1.8.0' dependencies: '@opentelemetry/api': 1.7.0 dev: false @@ -3665,16 +3661,6 @@ packages: '@opentelemetry/semantic-conventions': 1.18.1 dev: false - /@opentelemetry/core@1.27.0(@opentelemetry/api@1.7.0): - resolution: {integrity: sha512-yQPKnK5e+76XuiqUH/gKyS8wv/7qITd5ln56QkBTf3uggr0VkXOXfcaAuG330UfdYu83wsyoBwqwxigpIG+Jkg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - dependencies: - '@opentelemetry/api': 1.7.0 - '@opentelemetry/semantic-conventions': 1.27.0 - dev: false - /@opentelemetry/exporter-trace-otlp-http@0.45.1(@opentelemetry/api@1.7.0): resolution: {integrity: sha512-a6CGqSG66n5R1mghzLMzyzn3iGap1b0v+0PjKFjfYuwLtpHQBxh2PHxItu+m2mXSwnM4R0GJlk9oUW5sQkCE0w==} engines: {node: '>=14'} @@ -3745,24 +3731,24 @@ packages: '@opentelemetry/sdk-trace-base': 1.18.1(@opentelemetry/api@1.7.0) dev: false - /@opentelemetry/propagator-b3@1.27.0(@opentelemetry/api@1.7.0): - resolution: {integrity: sha512-pTsko3gnMioe3FeWcwTQR3omo5C35tYsKKwjgTCTVCgd3EOWL9BZrMfgLBmszrwXABDfUrlAEFN/0W0FfQGynQ==} + /@opentelemetry/propagator-b3@1.18.1(@opentelemetry/api@1.7.0): + resolution: {integrity: sha512-oSTUOsnt31JDx5SoEy27B5jE1/tiPvvE46w7CDKj0R5oZhCCfYH2bbSGa7NOOyDXDNqQDkgqU1DIV/xOd3f8pw==} engines: {node: '>=14'} peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/api': '>=1.0.0 <1.8.0' dependencies: '@opentelemetry/api': 1.7.0 - '@opentelemetry/core': 1.27.0(@opentelemetry/api@1.7.0) + '@opentelemetry/core': 1.18.1(@opentelemetry/api@1.7.0) dev: false - /@opentelemetry/propagator-jaeger@1.27.0(@opentelemetry/api@1.7.0): - resolution: {integrity: sha512-EI1bbK0wn0yIuKlc2Qv2LKBRw6LiUWevrjCF80fn/rlaB+7StAi8Y5s8DBqAYNpY7v1q86+NjU18v7hj2ejU3A==} + /@opentelemetry/propagator-jaeger@1.18.1(@opentelemetry/api@1.7.0): + resolution: {integrity: sha512-Kh4M1Qewv0Tbmts6D8LgNzx99IjdE18LCmY/utMkgVyU7Bg31Yuj+X6ZyoIRKPcD2EV4rVkuRI16WVMRuGbhWA==} engines: {node: '>=14'} peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/api': '>=1.0.0 <1.8.0' dependencies: '@opentelemetry/api': 1.7.0 - '@opentelemetry/core': 1.27.0(@opentelemetry/api@1.7.0) + '@opentelemetry/core': 1.18.1(@opentelemetry/api@1.7.0) dev: false /@opentelemetry/resources@1.18.1(@opentelemetry/api@1.7.0): @@ -3776,17 +3762,6 @@ packages: '@opentelemetry/semantic-conventions': 1.18.1 dev: false - /@opentelemetry/resources@1.27.0(@opentelemetry/api@1.7.0): - resolution: {integrity: sha512-jOwt2VJ/lUD5BLc+PMNymDrUCpm5PKi1E9oSVYAvz01U/VdndGmrtV3DU1pG4AwlYhJRHbHfOUIlpBeXCPw6QQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - dependencies: - '@opentelemetry/api': 1.7.0 - '@opentelemetry/core': 1.27.0(@opentelemetry/api@1.7.0) - '@opentelemetry/semantic-conventions': 1.27.0 - dev: false - /@opentelemetry/sdk-logs@0.45.1(@opentelemetry/api-logs@0.45.1)(@opentelemetry/api@1.7.0): resolution: {integrity: sha512-z0RRgW4LeKEKnhXS4F/HnqB6+7gsy63YK47F4XAJYHs4s1KKg8XnQ2RkbuL31i/a9nXkylttYtvsT50CGr487g==} engines: {node: '>=14'} @@ -3824,43 +3799,19 @@ packages: '@opentelemetry/semantic-conventions': 1.18.1 dev: false - /@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.7.0): - resolution: {integrity: sha512-btz6XTQzwsyJjombpeqCX6LhiMQYpzt2pIYNPnw0IPO/3AhT6yjnf8Mnv3ZC2A4eRYOjqrg+bfaXg9XHDRJDWQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - dependencies: - '@opentelemetry/api': 1.7.0 - '@opentelemetry/core': 1.27.0(@opentelemetry/api@1.7.0) - '@opentelemetry/resources': 1.27.0(@opentelemetry/api@1.7.0) - '@opentelemetry/semantic-conventions': 1.27.0 - dev: false - - /@opentelemetry/sdk-trace-node@1.27.0(@opentelemetry/api@1.7.0): - resolution: {integrity: sha512-dWZp/dVGdUEfRBjBq2BgNuBlFqHCxyyMc8FsN0NX15X07mxSUO0SZRLyK/fdAVrde8nqFI/FEdMH4rgU9fqJfQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - dependencies: - '@opentelemetry/api': 1.7.0 - '@opentelemetry/context-async-hooks': 1.27.0(@opentelemetry/api@1.7.0) - '@opentelemetry/core': 1.27.0(@opentelemetry/api@1.7.0) - '@opentelemetry/propagator-b3': 1.27.0(@opentelemetry/api@1.7.0) - '@opentelemetry/propagator-jaeger': 1.27.0(@opentelemetry/api@1.7.0) - '@opentelemetry/sdk-trace-base': 1.27.0(@opentelemetry/api@1.7.0) - semver: 7.6.3 - dev: false - - /@opentelemetry/sdk-trace-web@1.18.1(@opentelemetry/api@1.7.0): - resolution: {integrity: sha512-WN30vxy4NY8TqFWuICXaPXjBdy6A5kDhxOqp4NfhqXfpcWWT0GqSgv05Q42quWYOFgaulnmPRRJwxzAdhBliLQ==} + /@opentelemetry/sdk-trace-node@1.18.1(@opentelemetry/api@1.7.0): + resolution: {integrity: sha512-ML0l9TNlfLoplLF1F8lb95NGKgdm6OezDS3Ymqav9sYxMd5bnH2LZVzd4xEF+ov5vpZJOGdWxJMs2nC9no7+xA==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.8.0' dependencies: '@opentelemetry/api': 1.7.0 + '@opentelemetry/context-async-hooks': 1.18.1(@opentelemetry/api@1.7.0) '@opentelemetry/core': 1.18.1(@opentelemetry/api@1.7.0) + '@opentelemetry/propagator-b3': 1.18.1(@opentelemetry/api@1.7.0) + '@opentelemetry/propagator-jaeger': 1.18.1(@opentelemetry/api@1.7.0) '@opentelemetry/sdk-trace-base': 1.18.1(@opentelemetry/api@1.7.0) - '@opentelemetry/semantic-conventions': 1.18.1 + semver: 7.6.0 dev: false /@opentelemetry/semantic-conventions@1.18.1: @@ -3868,11 +3819,6 @@ packages: engines: {node: '>=14'} dev: false - /@opentelemetry/semantic-conventions@1.27.0: - resolution: {integrity: sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==} - engines: {node: '>=14'} - dev: false - /@opentelemetry/semantic-conventions@1.3.1: resolution: {integrity: sha512-wU5J8rUoo32oSef/rFpOT1HIjLjAv3qIDHkw1QIhODV3OpAVHi5oVzlouozg9obUmZKtbZ0qUe/m7FP0y0yBzA==} engines: {node: '>=8.12.0'} @@ -7242,7 +7188,7 @@ packages: esbuild: '>=0.10.0' dependencies: esbuild: 0.18.20 - tslib: 2.1.0 + tslib: 2.7.0 dev: true /@yarnpkg/fslib@2.10.3: @@ -7250,7 +7196,7 @@ packages: engines: {node: '>=12 <14 || 14.2 - 14.9 || >14.10.0'} dependencies: '@yarnpkg/libzip': 2.3.0 - tslib: 2.1.0 + tslib: 1.14.1 dev: true /@yarnpkg/libzip@2.3.0: @@ -7258,7 +7204,7 @@ packages: engines: {node: '>=12 <14 || 14.2 - 14.9 || >14.10.0'} dependencies: '@types/emscripten': 1.39.10 - tslib: 2.1.0 + tslib: 1.14.1 dev: true /@zkochan/rimraf@3.0.2: @@ -7484,7 +7430,7 @@ packages: resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==} engines: {node: '>=10'} dependencies: - tslib: 2.1.0 + tslib: 2.7.0 dev: false /aria-query@5.1.3: @@ -7568,14 +7514,14 @@ packages: resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} engines: {node: '>=4'} dependencies: - tslib: 2.1.0 + tslib: 2.7.0 dev: false /ast-types@0.16.1: resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} engines: {node: '>=4'} dependencies: - tslib: 2.1.0 + tslib: 2.7.0 dev: true /astral-regex@2.0.0: @@ -7586,7 +7532,7 @@ packages: /async-mutex@0.4.0: resolution: {integrity: sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA==} dependencies: - tslib: 2.1.0 + tslib: 2.7.0 dev: false /async@3.2.5: @@ -9156,7 +9102,7 @@ packages: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} dependencies: no-case: 3.0.4 - tslib: 2.1.0 + tslib: 2.7.0 dev: true /dotenv-expand@10.0.0: @@ -9333,7 +9279,7 @@ packages: dependencies: esbuild: 0.18.20 find-up: 5.0.0 - tslib: 2.1.0 + tslib: 2.7.0 dev: true /esbuild-plugin-alias@0.2.1: @@ -11954,7 +11900,7 @@ packages: /lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} dependencies: - tslib: 2.1.0 + tslib: 2.7.0 dev: true /lowlight@2.9.0: @@ -12992,7 +12938,7 @@ packages: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} dependencies: lower-case: 2.0.2 - tslib: 2.1.0 + tslib: 2.7.0 dev: true /nocache@3.0.4: @@ -14366,7 +14312,7 @@ packages: '@types/react': 18.2.37 react: 18.2.0 react-style-singleton: 2.2.1(@types/react@18.2.37)(react@18.2.0) - tslib: 2.1.0 + tslib: 2.7.0 dev: false /react-remove-scroll-bar@2.3.6(@types/react@18.2.79)(react@18.2.0): @@ -14382,7 +14328,7 @@ packages: '@types/react': 18.2.79 react: 18.2.0 react-style-singleton: 2.2.1(@types/react@18.2.79)(react@18.2.0) - tslib: 2.1.0 + tslib: 2.7.0 dev: false /react-remove-scroll@2.5.5(@types/react@18.2.37)(react@18.2.0): @@ -14399,7 +14345,7 @@ packages: react: 18.2.0 react-remove-scroll-bar: 2.3.6(@types/react@18.2.37)(react@18.2.0) react-style-singleton: 2.2.1(@types/react@18.2.37)(react@18.2.0) - tslib: 2.1.0 + tslib: 2.7.0 use-callback-ref: 1.3.2(@types/react@18.2.37)(react@18.2.0) use-sidecar: 1.1.2(@types/react@18.2.37)(react@18.2.0) dev: false @@ -14418,7 +14364,7 @@ packages: react: 18.2.0 react-remove-scroll-bar: 2.3.6(@types/react@18.2.79)(react@18.2.0) react-style-singleton: 2.2.1(@types/react@18.2.79)(react@18.2.0) - tslib: 2.1.0 + tslib: 2.7.0 use-callback-ref: 1.3.2(@types/react@18.2.79)(react@18.2.0) use-sidecar: 1.1.2(@types/react@18.2.79)(react@18.2.0) dev: false @@ -14437,7 +14383,7 @@ packages: get-nonce: 1.0.1 invariant: 2.2.4 react: 18.2.0 - tslib: 2.1.0 + tslib: 2.7.0 dev: false /react-style-singleton@2.2.1(@types/react@18.2.79)(react@18.2.0): @@ -14454,7 +14400,7 @@ packages: get-nonce: 1.0.1 invariant: 2.2.4 react: 18.2.0 - tslib: 2.1.0 + tslib: 2.7.0 dev: false /react@18.2.0: @@ -14564,7 +14510,7 @@ packages: esprima: 4.0.1 source-map: 0.6.1 tiny-invariant: 1.3.3 - tslib: 2.1.0 + tslib: 2.7.0 dev: true /redent@3.0.0: @@ -14932,7 +14878,7 @@ packages: /rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} dependencies: - tslib: 2.1.0 + tslib: 2.7.0 dev: true /safe-buffer@5.1.2: @@ -15199,7 +15145,7 @@ packages: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} dependencies: dot-case: 3.0.4 - tslib: 2.1.0 + tslib: 2.7.0 dev: true /snapdragon-node@2.1.1: @@ -16177,8 +16123,15 @@ packages: strip-bom: 3.0.0 dev: true + /tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + /tslib@2.1.0: resolution: {integrity: sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==} + dev: false + + /tslib@2.7.0: + resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} /tsscmp@1.0.6: resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==} @@ -16579,7 +16532,7 @@ packages: dependencies: '@types/react': 18.2.37 react: 18.2.0 - tslib: 2.1.0 + tslib: 2.7.0 dev: false /use-callback-ref@1.3.2(@types/react@18.2.79)(react@18.2.0): @@ -16594,7 +16547,7 @@ packages: dependencies: '@types/react': 18.2.79 react: 18.2.0 - tslib: 2.1.0 + tslib: 2.7.0 dev: false /use-sidecar@1.1.2(@types/react@18.2.37)(react@18.2.0): @@ -16610,7 +16563,7 @@ packages: '@types/react': 18.2.37 detect-node-es: 1.1.0 react: 18.2.0 - tslib: 2.1.0 + tslib: 2.7.0 dev: false /use-sidecar@1.1.2(@types/react@18.2.79)(react@18.2.0): @@ -16626,7 +16579,7 @@ packages: '@types/react': 18.2.79 detect-node-es: 1.1.0 react: 18.2.0 - tslib: 2.1.0 + tslib: 2.7.0 dev: false /use@3.1.1: @@ -17376,7 +17329,7 @@ packages: dev: false '@storage.googleapis.com/sourcegraph-assets/npm/lexical-react-sourcegraph-fork-31065486.tgz(react-dom@18.2.0)(react@18.2.0)(yjs@13.6.19)': - resolution: {registry: https://registry.npmjs.org/, tarball: https://storage.googleapis.com/sourcegraph-assets/npm/lexical-react-sourcegraph-fork-31065486.tgz} + resolution: {tarball: https://storage.googleapis.com/sourcegraph-assets/npm/lexical-react-sourcegraph-fork-31065486.tgz} id: '@storage.googleapis.com/sourcegraph-assets/npm/lexical-react-sourcegraph-fork-31065486.tgz' name: '@lexical/react' version: 0.17.0 diff --git a/vscode/src/chat/chat-view/ChatController.ts b/vscode/src/chat/chat-view/ChatController.ts index 641555e4372d..5ca072bcfd18 100644 --- a/vscode/src/chat/chat-view/ChatController.ts +++ b/vscode/src/chat/chat-view/ChatController.ts @@ -19,6 +19,9 @@ import { skipPendingOperation, wrapInActiveSpan, } from '@sourcegraph/cody-shared' +import * as uuid from 'uuid' +import * as vscode from 'vscode' + import { type BillingCategory, type BillingProduct, @@ -76,8 +79,6 @@ import { truncatePromptString, userProductSubscription, } from '@sourcegraph/cody-shared' -import * as uuid from 'uuid' -import * as vscode from 'vscode' import type { Span } from '@opentelemetry/api' import { captureException } from '@sentry/core' @@ -108,7 +109,6 @@ import { authProvider } from '../../services/AuthProvider' import { AuthProviderSimplified } from '../../services/AuthProviderSimplified' import { localStorage } from '../../services/LocalStorageProvider' import { secretStorage } from '../../services/SecretStorageProvider' -import { TraceSender } from '../../services/open-telemetry/trace-sender' import { recordExposedExperimentsToSpan } from '../../services/open-telemetry/utils' import { handleCodeFromInsertAtCursor, @@ -328,9 +328,6 @@ export class ChatController implements vscode.Disposable, vscode.WebviewViewProv message.fileName ) break - case 'trace-export': - TraceSender.send(message.traceSpanEncodedJson) - break case 'smartApplyAccept': await vscode.commands.executeCommand('cody.fixup.codelens.accept', message.id) break diff --git a/vscode/src/chat/protocol.ts b/vscode/src/chat/protocol.ts index 045904325f6d..561feb4e265e 100644 --- a/vscode/src/chat/protocol.ts +++ b/vscode/src/chat/protocol.ts @@ -97,11 +97,6 @@ export type WebviewMessage = instruction?: string | undefined | null fileName?: string | undefined | null } - | { - command: 'trace-export' - // The traceSpan is a JSON-encoded string representing the trace data. - traceSpanEncodedJson: string - } | { command: 'smartApplyAccept' id: FixupTaskID diff --git a/vscode/src/services/open-telemetry/CodyTraceExportWeb.ts b/vscode/src/services/open-telemetry/CodyTraceExportWeb.ts deleted file mode 100644 index 1a9326152009..000000000000 --- a/vscode/src/services/open-telemetry/CodyTraceExportWeb.ts +++ /dev/null @@ -1,191 +0,0 @@ -import type { ExportResult } from '@opentelemetry/core' -import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' -import type { ReadableSpan } from '@opentelemetry/sdk-trace-base' -import type { CodyIDE } from '@sourcegraph/cody-shared/src/configuration' -import { getVSCodeAPI } from '../../../webviews/utils/VSCodeApi' -import { logDebug } from '../../../webviews/utils/logger' - -const MAX_TRACE_RETAIN_MS = 60 * 1000 * 5 // 5 minutes - -// Exports spans as JSON to the extension host so that it can be sent to the OTel collector on the SG instance -export class CodyTraceExporterWeb extends OTLPTraceExporter { - private isTracingEnabled: boolean - private queuedSpans: Map = new Map() - private clientPlatform: CodyIDE - private agentVersion?: string - private lastExpiryCheck = 0 - - constructor({ - isTracingEnabled, - clientPlatform, - agentVersion, - }: { isTracingEnabled: boolean; clientPlatform: CodyIDE; agentVersion?: string }) { - super({ - httpAgentOptions: { - rejectUnauthorized: false, - }, - headers: { - 'Content-Type': 'application/json', - }, - }) - this.isTracingEnabled = isTracingEnabled - this.clientPlatform = clientPlatform - this.agentVersion = agentVersion - } - - private removeExpiredSpans(now: number): void { - for (const [spanId, { enqueuedAt }] of this.queuedSpans.entries()) { - if (now - enqueuedAt > MAX_TRACE_RETAIN_MS) { - this.queuedSpans.delete(spanId) - logDebug('[CodyTraceExporterWeb] Removed expired span from queue:', spanId) - } - } - } - - export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void): void { - if (!this.isTracingEnabled) { - return - } - - const now = performance.now() - if (now - this.lastExpiryCheck > MAX_TRACE_RETAIN_MS) { - this.removeExpiredSpans(now) - this.lastExpiryCheck = now - } - - // Include queued spans for re-evaluation - const allSpans = [...spans, ...Array.from(this.queuedSpans.values()).map(q => q.span)] - for (const span of allSpans) { - span.attributes.clientPlatform = this.clientPlatform - span.attributes.agentVersion = this.agentVersion - } - - // Build span hierarchy map - const spanMap = new Map() - const spansByRoot = new Map>() - - // First, map all spans by their ID - for (const span of allSpans) { - spanMap.set(span.spanContext().spanId, span) - } - - // Group spans by their root span - for (const span of spanMap.values()) { - const rootSpan = getRootSpan(spanMap, span) - if (rootSpan) { - const rootId = rootSpan.spanContext().spanId - if (!spansByRoot.has(rootId)) { - spansByRoot.set(rootId, new Set()) - } - spansByRoot.get(rootId)?.add(span) - } else { - // Queue spans without a root for later - const spanId = span.spanContext().spanId - if (!this.queuedSpans.has(spanId)) { - this.queuedSpans.set(spanId, { span, enqueuedAt: now }) - } - } - } - - const spansToExport: ReadableSpan[] = [] - - // Process each group of spans - for (const [rootId, spanGroup] of spansByRoot.entries()) { - const rootSpan = spanMap.get(rootId) - if (!rootSpan || !isSampled(rootSpan)) { - continue - } - - // Add all spans from complete groups - spansToExport.push(...spanGroup) - - // Remove these spans from queued spans if present - for (const span of spanGroup) { - this.queuedSpans.delete(span.spanContext().spanId) - logDebug('[CodyTraceExporterWeb] Removed span from queue:', span.spanContext().spanId) - } - } - if (spansToExport.length > 0) { - super.export(spansToExport, resultCallback) - } - } - - send(spans: ReadableSpan[]): void { - try { - const exportData = this.convert(spans) - - logDebug( - '[CodyTraceExporterWeb] Exporting spans', - JSON.stringify({ - count: spans.length, - rootSpans: spans.filter(s => !s.parentSpanId).length, - renderSpans: spans.filter(s => s.name === 'assistant-message-render').length, - }) - ) - - // Validate and clean the export data before sending - const messageData = { - resourceSpans: (exportData.resourceSpans ?? []).map(span => ({ - ...span, - resource: { - ...span?.resource, - attributes: - span?.resource?.attributes?.map(attr => ({ - key: attr.key, - value: attr.value, - })) ?? [], - }, - })), - timestamp: performance.now(), - } - - // Send the validated and cleaned data - getVSCodeAPI().postMessage({ - command: 'trace-export', - traceSpanEncodedJson: JSON.stringify(messageData, getCircularReplacer()), - }) - } catch (error) { - console.error('[CodyTraceExporterWeb] Error exporting spans:', error) - } - } -} - -function getRootSpan(spanMap: Map, span: ReadableSpan): ReadableSpan | null { - // Start with the input span - let currentSpan = span - - while (true) { - // If we find a span without a parent, it's the root - if (!currentSpan.parentSpanId) { - return currentSpan - } - - const parentSpan = spanMap.get(currentSpan.parentSpanId) - - // Return null if parent ID exists but parent span not found. - // These spans are expected to be completed later. - if (!parentSpan) { - return null - } - - currentSpan = parentSpan - } -} - -function isSampled(rootSpan: ReadableSpan): boolean { - return rootSpan.attributes.sampled === true -} - -// Helper function to handle circular references in JSON serialization -function getCircularReplacer() { - const seen = new WeakSet() - return (key: string, value: any) => { - if (typeof value === 'object' && value !== null) { - if (seen.has(value)) { - return - } - seen.add(value) - } - return value - } -} diff --git a/vscode/src/services/open-telemetry/trace-sender.ts b/vscode/src/services/open-telemetry/trace-sender.ts deleted file mode 100644 index d7e7bb410d82..000000000000 --- a/vscode/src/services/open-telemetry/trace-sender.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { currentResolvedConfig } from '@sourcegraph/cody-shared' -import fetch from 'node-fetch' -import { logDebug, logError } from '../../output-channel-logger' - -/** - * Sends trace data to the server without blocking - */ -export const TraceSender = { - send(spanData: any): void { - // Don't await - let it run in background, but do handle errors - void doSendTraceData(spanData).catch(error => { - logError('TraceSender', `Error sending trace data: ${error}`) - }) - }, -} - -/** - * Sends trace data to the server using the provided span data as a json string - * that comes from the webview. It retrieves the current resolved configuration to obtain - * authentication details and constructs the trace URL. It sends a POST - * request with the span data as the body. - */ -async function doSendTraceData(spanData: any): Promise { - const { auth } = await currentResolvedConfig() - if (!auth.accessToken) { - logError('TraceSender', 'Cannot send trace data: not authenticated') - throw new Error('Not authenticated') - } - - const traceUrl = new URL('/-/debug/otlp/v1/traces', auth.serverEndpoint).toString() - const response = await fetch(traceUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - ...(auth.accessToken ? { Authorization: `token ${auth.accessToken}` } : {}), - }, - body: spanData, - }) - - if (!response.ok) { - logError('TraceSender', `Failed to send trace data: ${response.statusText}`) - throw new Error(`Failed to send trace data: ${response.statusText}`) - } - - logDebug('TraceSender', 'Trace data sent successfully') -} diff --git a/vscode/webviews/App.tsx b/vscode/webviews/App.tsx index 95d11ebb9b8e..5853cf0a89db 100644 --- a/vscode/webviews/App.tsx +++ b/vscode/webviews/App.tsx @@ -1,5 +1,7 @@ import { type ComponentProps, useCallback, useEffect, useMemo, useState } from 'react' +import styles from './App.module.css' + import { type ChatMessage, type DefaultContext, @@ -8,11 +10,9 @@ import { type TelemetryRecorder, } from '@sourcegraph/cody-shared' import type { AuthMethod } from '../src/chat/protocol' -import styles from './App.module.css' import { AuthPage } from './AuthPage' import { LoadingPage } from './LoadingPage' import { useClientActionDispatcher } from './client/clientState' -import { WebviewOpenTelemetryService } from './utils/webviewOpenTelemetryService' import { ExtensionAPIProviderFromVSCodeAPI } from '@sourcegraph/prompt-editor' import { CodyPanel } from './CodyPanel' @@ -142,22 +142,6 @@ export const App: React.FunctionComponent<{ vscodeAPI: VSCodeWrapper }> = ({ vsc // V2 telemetry recorder const telemetryRecorder = useMemo(() => createWebviewTelemetryRecorder(vscodeAPI), [vscodeAPI]) - const webviewTelemetryService = useMemo(() => { - const service = WebviewOpenTelemetryService.getInstance() - return service - }, []) - - useEffect(() => { - if (config) { - webviewTelemetryService.configure({ - isTracingEnabled: true, - debugVerbose: true, - agentIDE: config.clientCapabilities.agentIDE, - extensionAgentVersion: config.clientCapabilities.agentExtensionVersion, - }) - } - }, [config, webviewTelemetryService]) - const wrappers = useMemo( () => getAppWrappers({ vscodeAPI, telemetryRecorder, config }), [vscodeAPI, telemetryRecorder, config] diff --git a/vscode/webviews/Chat.tsx b/vscode/webviews/Chat.tsx index 78ae80c9d7b5..502413f352dd 100644 --- a/vscode/webviews/Chat.tsx +++ b/vscode/webviews/Chat.tsx @@ -1,5 +1,5 @@ import type React from 'react' -import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { useCallback, useEffect, useMemo, useRef } from 'react' import type { AuthenticatedAuthStatus, @@ -12,7 +12,6 @@ import type { import { Transcript, focusLastHumanMessageEditor } from './chat/Transcript' import type { VSCodeWrapper } from './utils/VSCodeApi' -import type { Context } from '@opentelemetry/api' import { truncateTextStart } from '@sourcegraph/cody-shared/src/prompt/truncation' import { CHAT_INPUT_TOKEN_BUDGET } from '@sourcegraph/cody-shared/src/token/constants' import styles from './Chat.module.css' @@ -22,6 +21,7 @@ import { ScrollDown } from './components/ScrollDown' import type { View } from './tabs' import { useTelemetryRecorder } from './utils/telemetry' import { useUserAccountInfo } from './utils/useConfig' + interface ChatboxProps { chatEnabled: boolean messageInProgress: ChatMessage | null @@ -64,7 +64,6 @@ export const Chat: React.FunctionComponent thumbsUp = 1, thumbsDown = 0, } - telemetryRecorder.recordEvent('cody.feedback', 'submit', { metadata: { feedbackType: text === 'thumbsUp' ? FeedbackType.thumbsUp : FeedbackType.thumbsDown, @@ -93,10 +92,8 @@ export const Chat: React.FunctionComponent (text: string, eventType: 'Button' | 'Keydown' = 'Button') => { const op = 'copy' // remove the additional /n added by the text area at the end of the text - const code = eventType === 'Button' ? text.replace(/\n$/, '') : text // Log the event type and text to telemetry in chat view - vscodeAPI.postMessage({ command: op, eventType, @@ -111,7 +108,6 @@ export const Chat: React.FunctionComponent return (text: string, newFile = false) => { const op = newFile ? 'newFile' : 'insert' // Log the event type and text to telemetry in chat view - vscodeAPI.postMessage({ command: op, // remove the additional /n added by the text area at the end of the text @@ -213,7 +209,6 @@ export const Chat: React.FunctionComponent focusLastHumanMessageEditor() }, [transcript]) - const [activeChatContext, setActiveChatContext] = useState() return ( <> @@ -223,8 +218,6 @@ export const Chat: React.FunctionComponent )} = { postMessage: () => {}, chatEnabled: true, models: mockedModels, - setActiveChatContext: () => {}, } satisfies ComponentProps, decorators: [ diff --git a/vscode/webviews/chat/Transcript.test.tsx b/vscode/webviews/chat/Transcript.test.tsx index 7f6990a51119..25ca14a1d7cb 100644 --- a/vscode/webviews/chat/Transcript.test.tsx +++ b/vscode/webviews/chat/Transcript.test.tsx @@ -23,7 +23,6 @@ const PROPS: Omit, 'transcript'> = { chatEnabled: true, postMessage: () => {}, models: MOCK_MODELS, - setActiveChatContext: () => {}, } vi.mock('@vscode/webview-ui-toolkit/react', () => ({ diff --git a/vscode/webviews/chat/Transcript.tsx b/vscode/webviews/chat/Transcript.tsx index 84549055f82e..0f3ee527d5ee 100644 --- a/vscode/webviews/chat/Transcript.tsx +++ b/vscode/webviews/chat/Transcript.tsx @@ -23,18 +23,15 @@ import { type MutableRefObject, memo, useCallback, - useEffect, useImperativeHandle, useMemo, useRef, - useState, } from 'react' import { URI } from 'vscode-uri' import type { UserAccountInfo } from '../Chat' import type { ApiPostMessage } from '../Chat' import { Button } from '../components/shadcn/ui/button' import { getVSCodeAPI } from '../utils/VSCodeApi' -import { SpanManager } from '../utils/spanManager' import { useTelemetryRecorder } from '../utils/telemetry' import { useExperimentalOneBox } from '../utils/useExperimentalOneBox' import type { CodeBlockActionsProps } from './ChatMessageContent/ChatMessageContent' @@ -51,15 +48,13 @@ import { HumanMessageCell } from './cells/messageCell/human/HumanMessageCell' import { CodyIcon } from './components/CodyIcon' import { InfoMessage } from './components/InfoMessage' -import { type Context, type Span, context, trace } from '@opentelemetry/api' interface TranscriptProps { - activeChatContext?: Context - setActiveChatContext: (context: Context | undefined) => void chatEnabled: boolean transcript: ChatMessage[] models: Model[] userInfo: UserAccountInfo messageInProgress: ChatMessage | null + guardrails?: Guardrails postMessage?: ApiPostMessage @@ -72,8 +67,6 @@ interface TranscriptProps { export const Transcript: FC = props => { const { - activeChatContext, - setActiveChatContext, chatEnabled, transcript, models, @@ -137,8 +130,6 @@ export const Transcript: FC = props => { {interactions.map((interaction, i) => ( { - activeChatContext: Context | undefined - setActiveChatContext: (context: Context | undefined) => void interaction: Interaction isFirstInteraction: boolean isLastInteraction: boolean @@ -262,6 +251,7 @@ const TranscriptInteraction: FC = memo(props => { editorRef: parentEditorRef, onAddToFollowupChat, } = props + const [intentResults, setIntentResults] = useMutatedValue< | { intent: ChatMessage['intent'] @@ -271,100 +261,58 @@ const TranscriptInteraction: FC = memo(props => { | null >() - const { activeChatContext, setActiveChatContext } = props const humanEditorRef = useRef(null) useImperativeHandle(parentEditorRef, () => humanEditorRef.current) - const onUserAction = (action: 'edit' | 'submit', intentFromSubmit?: ChatMessage['intent']) => { - // Start the span as soon as the user initiates the action - const startMark = performance.mark('startSubmit') - const spanManager = new SpanManager('cody-webview') - const span = spanManager.startSpan('chat-interaction', { - attributes: { - sampled: true, - 'render.state': 'started', - 'startSubmit.mark': startMark.startTime, - }, - }) - - if (!span) { - throw new Error('Failed to start span for chat interaction') - } - - const spanContext = trace.setSpan(context.active(), span) - setActiveChatContext(spanContext) - - // Serialize the editor value after starting the span - const editorValue = humanEditorRef.current?.getSerializedValue() - if (!editorValue) { - console.error('Failed to serialize editor value') - return - } - - const commonProps = { - editorValue, - intent: intentFromSubmit || intentResults.current?.intent, - intentScores: intentFromSubmit ? undefined : intentResults.current?.allScores, - manuallySelectedIntent: !!intentFromSubmit, - } - - if (action === 'edit') { + const onEditSubmit = useCallback( + (editorValue: SerializedPromptEditorValue, intentFromSubmit?: ChatMessage['intent']): void => { editHumanMessage({ messageIndexInTranscript: humanMessage.index, - ...commonProps, + editorValue, + intent: intentFromSubmit || intentResults.current?.intent, + intentScores: intentFromSubmit ? undefined : intentResults.current?.allScores, + manuallySelectedIntent: !!intentFromSubmit, }) - } else { - submitHumanMessage({ - ...commonProps, - }) - } - } - - const onEditSubmit = useCallback( - (intentFromSubmit?: ChatMessage['intent']): void => { - onUserAction('edit', intentFromSubmit) }, - [onUserAction] + [humanMessage.index, intentResults] ) const onFollowupSubmit = useCallback( - (intentFromSubmit?: ChatMessage['intent']): void => { - onUserAction('submit', intentFromSubmit) + (editorValue: SerializedPromptEditorValue, intentFromSubmit?: ChatMessage['intent']): void => { + submitHumanMessage({ + editorValue, + intent: intentFromSubmit || intentResults.current?.intent, + intentScores: intentFromSubmit ? undefined : intentResults.current?.allScores, + manuallySelectedIntent: !!intentFromSubmit, + }) }, - [onUserAction] + [intentResults] ) const extensionAPI = useExtensionAPI() const experimentalOneBoxEnabled = useExperimentalOneBox() const onChange = useMemo(() => { return debounce(async (editorValue: SerializedPromptEditorValue) => { + setIntentResults(undefined) + if (!experimentalOneBoxEnabled) { return } + // Only detect intent if a repository is mentioned if ( - !editorValue.contextItems.find(contextItem => + editorValue.contextItems.find(contextItem => ['repository', 'tree'].includes(contextItem.type) ) ) { - return - } - - setIntentResults(undefined) - - const subscription = extensionAPI - .detectIntent(inputTextWithoutContextChipsFromPromptEditorState(editorValue.editorState)) - .subscribe({ - next: value => { + extensionAPI + .detectIntent( + inputTextWithoutContextChipsFromPromptEditorState(editorValue.editorState) + ) + .subscribe(value => { setIntentResults(value) - }, - error: error => { - console.error('Error detecting intent:', error) - }, - }) - - // Clean up subscription if component unmounts - return () => subscription.unsubscribe() + }) + } }, 300) }, [experimentalOneBoxEnabled, extensionAPI, setIntentResults]) @@ -379,112 +327,6 @@ const TranscriptInteraction: FC = memo(props => { isLastSentInteraction && assistantMessage?.text === undefined ) - const spanManager = new SpanManager('cody-webview') - const renderSpan = useRef() - const timeToFirstTokenSpan = useRef() - const hasRecordedFirstToken = useRef(false) - - const [isLoading, setIsLoading] = useState(assistantMessage?.isLoading) - - useEffect(() => { - setIsLoading(assistantMessage?.isLoading) - }, [assistantMessage]) - - useEffect(() => { - if (!assistantMessage) return - - const startRenderSpan = () => { - // Reset the spans to their initial state - renderSpan.current = undefined - timeToFirstTokenSpan.current = undefined - hasRecordedFirstToken.current = false - - const startRenderMark = performance.mark('startRender') - // Start a new span for rendering the assistant message - renderSpan.current = spanManager.startSpan('assistant-message-render', { - attributes: { - sampled: true, - 'message.index': assistantMessage.index, - 'render.start_time': startRenderMark.startTime, - 'parent.span.id': activeChatContext - ? trace.getSpan(activeChatContext)?.spanContext().spanId - : undefined, - }, - context: activeChatContext, - }) - // Start a span to measure time to first token - timeToFirstTokenSpan.current = spanManager.startSpan('time-to-first-token', { - attributes: { 'message.index': assistantMessage.index }, - context: activeChatContext, - }) - } - - const endRenderSpan = () => { - // Mark the end of rendering - performance.mark('endRender') - // Measure the duration of the render - const measure = performance.measure('renderDuration', 'startRender', 'endRender') - if (renderSpan.current && measure.duration > 0) { - // Set attributes and end the render span - renderSpan.current.setAttributes({ - 'render.success': !assistantMessage?.error, - 'message.length': assistantMessage?.text?.length ?? 0, - 'render.total_time': measure.duration, - }) - renderSpan.current.end() - } - renderSpan.current = undefined - hasRecordedFirstToken.current = false - - if (activeChatContext) { - const rootSpan = trace.getSpan(activeChatContext) - if (rootSpan) { - // Calculate and set the total chat time - const chatTotalTime = - performance.now() - performance.getEntriesByName('startSubmit')[0].startTime - rootSpan.setAttributes({ - 'chat.completed': true, - 'render.state': 'completed', - 'chat.total_time': chatTotalTime, - }) - rootSpan.end() - } - } - // Clear the active chat context - setActiveChatContext(undefined) - } - - const endFirstTokenSpan = () => { - if (renderSpan.current && timeToFirstTokenSpan.current) { - // Mark the first token - performance.mark('firstToken') - // Measure the time to first token - performance.measure('timeToFirstToken', 'startRender', 'firstToken') - const firstTokenMeasure = performance.getEntriesByName('timeToFirstToken')[0] - if (firstTokenMeasure.duration > 0) { - // Set attributes and end the time-to-first-token span - timeToFirstTokenSpan.current.setAttributes({ - 'time.to.first.token': firstTokenMeasure.duration, - }) - timeToFirstTokenSpan.current.end() - timeToFirstTokenSpan.current = undefined - hasRecordedFirstToken.current = true - } - } - } - // Case 3: End the time-to-first-token span when the first token appears - if (assistantMessage.text && !hasRecordedFirstToken.current && timeToFirstTokenSpan.current) { - endFirstTokenSpan() - } - // Case 1: Start rendering if the assistant message is loading and no render span exists - if (assistantMessage.isLoading && !renderSpan.current && activeChatContext) { - context.with(activeChatContext, startRenderSpan) - } - // Case 2: End rendering if loading is complete and a render span exists - else if (!isLoading && renderSpan.current) { - endRenderSpan() - } - }, [assistantMessage, activeChatContext, setActiveChatContext, spanManager, isLoading]) const humanMessageInfo = useMemo(() => { // See SRCH-942: it's critical to memoize this value to avoid repeated @@ -500,7 +342,7 @@ const TranscriptInteraction: FC = memo(props => { (intent: ChatMessage['intent']) => { const editorState = humanEditorRef.current?.getSerializedValue() if (editorState) { - onEditSubmit(intent) + onEditSubmit(editorState, intent) telemetryRecorder.recordEvent('onebox.intentCorrection', 'clicked', { metadata: { recordsPrivateMetadataTranscript: 1, @@ -525,7 +367,10 @@ const TranscriptInteraction: FC = memo(props => { return } await editor.addMentions(corpusContextItems, 'before', ' ') - onEditSubmit('chat') + const newEditorState = humanEditorRef.current?.getSerializedValue() + if (newEditorState) { + onEditSubmit(newEditorState, 'chat') + } } }, [corpusContextItems, onEditSubmit]) @@ -563,9 +408,7 @@ const TranscriptInteraction: FC = memo(props => { isSent={!humanMessage.isUnsentFollowup} isPendingPriorResponse={priorAssistantMessageIsLoading} onChange={onChange} - onSubmit={ - humanMessage.isUnsentFollowup ? () => onFollowupSubmit() : () => onEditSubmit() - } + onSubmit={humanMessage.isUnsentFollowup ? onFollowupSubmit : onEditSubmit} onStop={onStop} isFirstInteraction={isFirstInteraction} isLastInteraction={isLastInteraction} diff --git a/vscode/webviews/utils/spanManager.ts b/vscode/webviews/utils/spanManager.ts deleted file mode 100644 index 9841644e6f36..000000000000 --- a/vscode/webviews/utils/spanManager.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { - type Attributes, - type Context, - type Span, - type SpanOptions, - SpanStatusCode, - type Tracer, - context, - trace, -} from '@opentelemetry/api' - -// Extend SpanOptions to optionally include context -type SpanManagerOptions = SpanOptions & { - context?: Context -} - -/** - * SpanManager is responsible for managing the lifecycle of spans used in tracing. - * It provides methods to start, end, and manage spans, as well as to handle context propagation. - * - * Features: - * - Start and manage active spans with context propagation. - * - End spans and record exceptions. - * - Set attributes on spans. - * - Clear all spans and reset the active context. - */ -export class SpanManager { - private spans = new Map() - private endedSpans = new Set() - private tracer: Tracer - private activeContext?: Context - - constructor(tracerName = 'cody-webview') { - this.tracer = trace.getTracer(tracerName) - } - - startActiveSpan( - name: string, - optionsOrFn: SpanManagerOptions | ((span: Span) => Promise | T), - fnOrUndefined?: (span: Span) => Promise | T - ): Promise { - const options = typeof optionsOrFn === 'function' ? {} : optionsOrFn - const fn = typeof optionsOrFn === 'function' ? optionsOrFn : fnOrUndefined - - if (!fn) { - throw new Error('No callback function provided to startActiveSpan') - } - - // Context is optional - if not provided, use active context - const parentContext = options.context || this.activeContext || context.active() - - // Extract standard SpanOptions from SpanManagerOptions - const spanOptions: SpanOptions = { - attributes: options.attributes, - kind: options.kind, - links: options.links, - startTime: options.startTime, - } - - return this.tracer.startActiveSpan(name, spanOptions, async span => { - this.spans.set(name, span) - - // Create new context with this span - const spanContext = trace.setSpan(parentContext, span) - this.activeContext = spanContext - - try { - return await context.with(spanContext, () => fn(span)) - } catch (error) { - span.setStatus({ - code: SpanStatusCode.ERROR, - message: error instanceof Error ? error.message : 'Unknown error', - }) - span.recordException(error as Error) - throw error - } finally { - this.endSpan(name) - } - }) - } - - startSpan(name: string, options?: SpanManagerOptions): Span | undefined { - if (this.spans.has(name)) { - return this.spans.get(name) - } - - // Use provided context or fall back to active context - const parentContext = options?.context || this.activeContext || context.active() - - // Extract standard SpanOptions from SpanManagerOptions - const spanOptions: SpanOptions = { - attributes: options?.attributes, - kind: options?.kind, - links: options?.links, - startTime: options?.startTime, - } - - const span = this.tracer.startSpan(name, spanOptions, parentContext) - this.spans.set(name, span) - return span - } - - getActiveContext(): Context | undefined { - return this.activeContext - } - - setActiveContext(ctx: Context): void { - this.activeContext = ctx - } - - endSpan(name: string): void { - const span = this.spans.get(name) - if (span && !this.endedSpans.has(name)) { - span.end() - this.endedSpans.add(name) - this.spans.delete(name) - } - } - - setSpanAttributes(name: string, attributes: Record): void { - const span = this.spans.get(name) - if (span && !this.endedSpans.has(name)) { - span.setAttributes(attributes as Attributes) - } - } - - endAllSpans(): void { - this.spans.forEach((_, name) => this.endSpan(name)) - } - - clear(): void { - this.endAllSpans() - this.spans.clear() - this.endedSpans.clear() - this.activeContext = undefined - } -} diff --git a/vscode/webviews/utils/webviewOpenTelemetryService.ts b/vscode/webviews/utils/webviewOpenTelemetryService.ts deleted file mode 100644 index ca73234b90c6..000000000000 --- a/vscode/webviews/utils/webviewOpenTelemetryService.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { DiagConsoleLogger, DiagLogLevel, diag } from '@opentelemetry/api' -import { Resource } from '@opentelemetry/resources' -import { BatchSpanProcessor, WebTracerProvider } from '@opentelemetry/sdk-trace-web' -import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions' -import type { CodyIDE } from '@sourcegraph/cody-shared/src/configuration' -import { CodyTraceExporterWeb } from '../../src/services/open-telemetry/CodyTraceExportWeb' - -// This class is used to initialize and manage the OpenTelemetry service for the webview. -// Its inspired by the OpenTelemetryService class in the node extension. -// It is used to initialize the tracer provider and add a span processor that exports the spans to the webview. -export class WebviewOpenTelemetryService { - private static instance: WebviewOpenTelemetryService | null = null - private tracerProvider?: WebTracerProvider - private unloadInstrumentations?: () => void - private isTracingEnabled = false - private isInitialized = false - private agentIDE?: CodyIDE - private extensionAgentVersion?: string - constructor() { - if (!WebviewOpenTelemetryService.instance) { - WebviewOpenTelemetryService.instance = this - this.reset() - } - } - - public configure(options?: { - isTracingEnabled?: boolean - debugVerbose?: boolean - agentIDE?: CodyIDE - extensionAgentVersion?: string - }): void { - // If the service is already initialized or if it is not the instance that is being used, return - if (this.isInitialized || WebviewOpenTelemetryService.instance !== this) { - return - } - - const { - isTracingEnabled = true, - debugVerbose = false, - agentIDE, - extensionAgentVersion, - } = options || {} - this.isTracingEnabled = isTracingEnabled - this.agentIDE = agentIDE - this.extensionAgentVersion = extensionAgentVersion - const logLevel = debugVerbose ? DiagLogLevel.INFO : DiagLogLevel.ERROR - diag.setLogger(new DiagConsoleLogger(), logLevel) - - try { - this.tracerProvider = new WebTracerProvider({ - resource: new Resource({ - [SemanticResourceAttributes.SERVICE_NAME]: 'cody-webview', - [SemanticResourceAttributes.SERVICE_VERSION]: '1.0.0', - }), - }) - if (this.isTracingEnabled) { - this.tracerProvider.addSpanProcessor( - new BatchSpanProcessor( - new CodyTraceExporterWeb({ - isTracingEnabled: true, - clientPlatform: this.agentIDE ?? ('defaultIDE' as CodyIDE), - agentVersion: this.extensionAgentVersion, - }) - ) - ) - } - - this.tracerProvider.register() - this.isInitialized = true - console.log('WebviewOpenTelemetryService initialized') - } catch (error) { - console.error('Failed to initialize OpenTelemetry:', error) - this.reset() - } - } - - public reset(): void { - if (this.tracerProvider) { - this.unloadInstrumentations?.() - this.tracerProvider.shutdown() - this.tracerProvider = undefined - this.isInitialized = false - } - } - - public dispose(): void { - if (WebviewOpenTelemetryService.instance !== this) { - return - } - this.reset() - WebviewOpenTelemetryService.instance = null - } - - public static getInstance(): WebviewOpenTelemetryService { - if (!WebviewOpenTelemetryService.instance) { - WebviewOpenTelemetryService.instance = new WebviewOpenTelemetryService() - } - return WebviewOpenTelemetryService.instance - } -} diff --git a/web/lib/agent/shims/stream.ts b/web/lib/agent/shims/stream.ts index f43b47597158..f81e6a99769a 100644 --- a/web/lib/agent/shims/stream.ts +++ b/web/lib/agent/shims/stream.ts @@ -5,7 +5,3 @@ export function Readable(): unknown { export function Writable(): unknown { return null } - -export default { - Readable: { prototype: {} }, -} diff --git a/web/lib/agent/shims/zlib.ts b/web/lib/agent/shims/zlib.ts deleted file mode 100644 index 22b5c12d9dd8..000000000000 --- a/web/lib/agent/shims/zlib.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default { - Readable: { prototype: {} }, -} diff --git a/web/vite.config.ts b/web/vite.config.ts index 96fb8faf0a9d..6d5af6875dc1 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -57,8 +57,6 @@ export default defineProjectWithDefaults(__dirname, { replacement: resolve(__dirname, 'node_modules/path-browserify'), }, { find: 'node:stream', replacement: resolve(__dirname, 'node_modules/stream-browserify') }, - { find: 'zlib', replacement: resolve(__dirname, 'lib/agent/shims/zlib.ts') }, - { find: 'stream', replacement: resolve(__dirname, 'lib/agent/shims/stream.ts') }, { find: /^(node:)?events$/, replacement: resolve(__dirname, 'node_modules/events') }, { find: /^(node:)?util$/, replacement: resolve(__dirname, 'node_modules/util') }, { find: /^(node:)?buffer$/, replacement: resolve(__dirname, 'node_modules/buffer') },