cherry-studio/packages/mcp-trace/trace-web/traceContextPromise.ts
alickreborn0 3b123863b5
feat: Support LLM Tracing by Alibaba Cloud EDAS product (#7895)
* feat: add tracing modules

* Initial commit

* fix: problem

* fix: update trace web

* fix: trace view

* fix: trace view

* fix: fix some problem

* fix: knowledge and mcp trace

* feat: save trace to user home dir

* feat: open trace with electron browser window

* fix: root trace outputs

* feat: trace internationalization and add trace icon

* feat: add trace title

* feat: update

* package.json添加windows运行script

* feat: update window title

* fix: mcp trace param

* fix: error show

* fix: listTool result

* fix: merge error

* feat: add stream usage and response

* feat: change trace stream

* fix: change stream adapter

* fix: span detail show problem

* fix: process show by time

* fix: stream outputs

* fix: merge problem

* fix: stream outputs

* fix: output text

* fix: EDAS support text

* fix: change trace footer style

* fix: topicId is loaded multiple times

* fix: span reload problem & attribute with cache

* fix: refresh optimization

* Change Powered by text.

* resolve upstream conflicts

* fix: build-time type exception

* fix: exceptions not used when building

* fix: recend no trace

* fix: resend trace list

* fix: delete temporary files

* feat: trace for resend

* fix: trace for resend message with edit

* fix: directory structure and construction method of mcp-trace

* fix: change CRLF to LF

* fix: add function call outputs

* Revert "fix: change CRLF to LF"

* fix: reorganize multi-model display

* fix: append model trace binding topic

* fix: some problems

* fix: code optimization

* fix: delete async

* fix: UI optimization

* fix: sort import

---------

Co-authored-by: 崔顺发 <csf01409784@alibaba-inc.com>
Co-authored-by: 管鑫荣 <gxr01409783@alibaba-inc.com>
2025-07-20 14:53:35 +08:00

100 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Context, context } from '@opentelemetry/api'
const originalPromise = globalThis.Promise
class TraceContextPromise<T> extends Promise<T> {
_context: Context
constructor(
executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void,
ctx?: Context
) {
const capturedContext = ctx || context.active()
super((resolve, reject) => {
context.with(capturedContext, () => {
executor(
(value) => context.with(capturedContext, () => resolve(value)),
(reason) => context.with(capturedContext, () => reject(reason))
)
})
})
this._context = capturedContext
}
// 兼容 Promise.resolve/reject
static resolve(): Promise<void>
static resolve<T>(value: T | PromiseLike<T>): Promise<T>
static resolve<T>(value: T | PromiseLike<T>, ctx?: Context): Promise<T>
static resolve<T>(value?: T | PromiseLike<T>, ctx?: Context): Promise<T | void> {
return new TraceContextPromise<T | void>((resolve) => resolve(value as T), ctx)
}
static reject<T = never>(reason?: any): Promise<T>
static reject<T = never>(reason?: any, ctx?: Context): Promise<T> {
return new TraceContextPromise<T>((_, reject) => reject(reason), ctx)
}
static all<T>(values: (T | PromiseLike<T>)[]): Promise<T[]> {
// 尝试从缓存获取 context
let capturedContext = context.active()
const newValues = values.map((v) => {
if (v instanceof Promise && !(v instanceof TraceContextPromise)) {
return new TraceContextPromise((resolve, reject) => v.then(resolve, reject), capturedContext)
} else if (typeof v === 'function') {
// 如果 v 是一个 Function使用 context 传递 trace 上下文
return (...args: any[]) => context.with(capturedContext, () => v(...args))
} else {
return v
}
})
if (Array.isArray(values) && values.length > 0 && values[0] instanceof TraceContextPromise) {
capturedContext = (values[0] as TraceContextPromise<any>)._context
}
return originalPromise.all(newValues) as Promise<T[]>
}
static race<T>(values: (T | PromiseLike<T>)[]): Promise<T> {
const capturedContext = context.active()
return new TraceContextPromise<T>((resolve, reject) => {
originalPromise.race(values).then(
(result) => context.with(capturedContext, () => resolve(result)),
(err) => context.with(capturedContext, () => reject(err))
)
}, capturedContext)
}
static allSettled<T>(values: (T | PromiseLike<T>)[]): Promise<PromiseSettledResult<T>[]> {
const capturedContext = context.active()
return new TraceContextPromise<PromiseSettledResult<T>[]>((resolve, reject) => {
originalPromise.allSettled(values).then(
(result) => context.with(capturedContext, () => resolve(result)),
(err) => context.with(capturedContext, () => reject(err))
)
}, capturedContext)
}
static any<T>(values: (T | PromiseLike<T>)[]): Promise<T> {
const capturedContext = context.active()
return new TraceContextPromise<T>((resolve, reject) => {
originalPromise.any(values).then(
(result) => context.with(capturedContext, () => resolve(result)),
(err) => context.with(capturedContext, () => reject(err))
)
}, capturedContext)
}
}
/**
* 用 TraceContextPromise 替换全局 Promise
*/
export function instrumentPromises() {
globalThis.Promise = TraceContextPromise as unknown as PromiseConstructor
}
/**
* 恢复原生 Promise
*/
export function uninstrumentPromises() {
globalThis.Promise = originalPromise
}