From c0d4c9043a16f1bbe7de50faf0fc203b58143b2a Mon Sep 17 00:00:00 2001 From: RainbowBird Date: Fri, 27 Mar 2026 18:25:12 +0800 Subject: [PATCH] docs(server): ai context --- apps/server/README.md | 27 +++ apps/server/docs/ai-context/README.md | 9 + .../config-and-naming-conventions.md | 161 +++++++++++++++ .../ai-context/observability-conventions.md | 159 +++++++++++++++ .../ai-context/redis-boundaries-and-pubsub.md | 188 ++++++++++++++++++ .../docs/ai-context/transport-and-routes.md | 21 +- .../docs/ai-context/workers-and-runtime.md | 2 + apps/server/tsconfig.json | 10 - 8 files changed, 558 insertions(+), 19 deletions(-) create mode 100644 apps/server/README.md create mode 100644 apps/server/docs/ai-context/config-and-naming-conventions.md create mode 100644 apps/server/docs/ai-context/observability-conventions.md create mode 100644 apps/server/docs/ai-context/redis-boundaries-and-pubsub.md diff --git a/apps/server/README.md b/apps/server/README.md new file mode 100644 index 000000000..72e0de163 --- /dev/null +++ b/apps/server/README.md @@ -0,0 +1,27 @@ +# `@proj-airi/server` + +HTTP and WebSocket backend for AIRI. This app owns auth, billing, chat synchronization, gateway forwarding, and server-side observability export. + +## What It Does + +- Serves the Hono-based API and WebSocket endpoints. +- Uses Postgres as the source of truth for users, billing, and durable state. +- Uses Redis for cache, KV, Pub/Sub, and Streams. +- Forwards GenAI requests to the configured upstream gateway and records billing from usage. +- Exports traces, metrics, and logs through OpenTelemetry. + +## How To Use It + +Install dependencies from the repo root and run scoped commands: + +```sh +pnpm -F @proj-airi/server typecheck +pnpm -F @proj-airi/server exec vitest run +pnpm -F @proj-airi/server build +``` + +For local observability infrastructure, use: + +```sh +docker compose -f apps/server/docker-compose.otel.yml up -d +``` diff --git a/apps/server/docs/ai-context/README.md b/apps/server/docs/ai-context/README.md index 6c80a77cd..fece4ebc7 100644 --- a/apps/server/docs/ai-context/README.md +++ b/apps/server/docs/ai-context/README.md @@ -17,8 +17,14 @@ - 主要表、状态归属、缓存与事件模型 - `workers-and-runtime.md` - CLI 角色、outbox dispatcher、Redis Streams consumer、运行时约束 +- `redis-boundaries-and-pubsub.md` + - Redis key / channel 收口、Pub/Sub / Streams 边界、运行时校验约束 +- `config-and-naming-conventions.md` + - `configKV` 默认值来源、Redis key 命名、HTTP route 命名、后续收敛 TODO - `billing-architecture.md` - 计费链路专项说明,重点看 Flux / Stripe / outbox / Redis Streams +- `observability-conventions.md` + - traces / metrics 命名规则,标准 OTel 字段与 `airi.*` 自定义字段边界 ## 快速结论 @@ -35,4 +41,7 @@ - 改某个接口行为:先看 `transport-and-routes.md` - 改表结构、缓存或幂等:先看 `data-model-and-state.md` - 改 worker、部署角色、事件处理:先看 `workers-and-runtime.md` +- 改 Redis key、Pub/Sub、Streams 边界:先看 `redis-boundaries-and-pubsub.md` +- 改配置默认值、Redis key 命名、HTTP route 命名:先看 `config-and-naming-conventions.md` - 改扣费、充值、Stripe:先看 `billing-architecture.md` +- 改 trace / metric attributes、OTel 命名:先看 `observability-conventions.md` diff --git a/apps/server/docs/ai-context/config-and-naming-conventions.md b/apps/server/docs/ai-context/config-and-naming-conventions.md new file mode 100644 index 000000000..6e1334ae4 --- /dev/null +++ b/apps/server/docs/ai-context/config-and-naming-conventions.md @@ -0,0 +1,161 @@ +# Config And Naming Conventions + +## 目标 + +这篇文档收口三类容易逐步漂移的约定: + +- `configKV` 的默认值和读取语义 +- Redis key / channel 的命名规则 +- HTTP route 的资源命名规则 + +这些约定不是“代码风格建议”,而是为了减少: + +- 默认值写两份导致的配置漂移 +- Redis key 命名混用导致的排障成本 +- HTTP route 语义不稳定导致的版本化困难 + +## `configKV` 约定 + +### 单一真相源 + +`src/services/config-kv.ts` 中的 `ConfigEntrySchemas` 是以下三件事的单一真相源: + +- 配置值的运行时校验 +- 配置值的默认值 +- Redis 中的序列化 / 反序列化 shape + +这意味着: + +- 默认值必须定义在 `ConfigEntrySchemas` +- 业务代码不要再写第二份 `?? defaultValue` +- `configKV.get()` / `configKV.getOrThrow()` 应直接依赖 schema 默认值 + +### 读取语义 + +- `getOptional(key)` + - 用于“这个 key 合法地可以不存在”的场景 + - 对 required key,未配置时返回 `null` + - 对带 schema 默认值的 key,返回默认值 +- `getOrThrow(key)` + - 用于“缺失就是配置错误”的场景 + - required key 未配置时抛 `CONFIG_NOT_SET` +- `get(key)` + - 是 `getOrThrow(key)` 的别名 + - 默认用于业务代码 + +### 禁止事项 + +- 不要给 `getOptional` 增加调用点默认值参数,例如 `getOptional(key, fallback)` +- 不要同时在 schema 和调用侧维护两份默认值 +- 不要绕过 `configKV` 直接从 Redis 读配置 + +### 当前例子 + +推荐: + +```ts +const fluxPer1kTokens = await configKV.get('FLUX_PER_1K_TOKENS') +const maxCheckoutAmount = await configKV.get('MAX_CHECKOUT_AMOUNT_CENTS') +``` + +不推荐: + +```ts +const fluxPer1kTokens = (await configKV.getOptional('FLUX_PER_1K_TOKENS')) ?? 1 +const maxCheckoutAmount = (await configKV.getOptional('MAX_CHECKOUT_AMOUNT_CENTS')) ?? 1_000_000 +``` + +## Redis key / channel 命名 + +### 命名规则 + +Redis key 和 channel 统一采用“分段命名”,推荐使用冒号 `:` 作为分隔符: + +```txt +{scope}:{id}:{resource} +{scope}:{id}:{subscope}:{subid}:{resource} +lock:{domain}:{id} +config:{key} +``` + +推荐例子: + +```txt +user:{userId}:flux +config:{key} +chat:{userId}:broadcast +lock:user:{userId}:flux +``` + +不推荐例子: + +```txt +flux:{userId} +chat:broadcast:{userId} +userFlux:{userId} +userUidFlux1 +``` + +### 设计原则 + +- 前缀表达 namespace,而不是随手缩写 +- 真实 Redis key 不要出现 `1`、`2` 这种占位编号 +- 参数占位编号只用于文档里的模板名,不用于运行时 key +- key / channel 必须通过 helper 收口,不要在业务代码里散落模板字符串 + +### 文档里的模板命名 + +如果要在文档中表示“这个 key 有几个参数位”,可以用编号描述模板: + +- `userUserId1Flux` +- `configKey1` +- `lockDomain1Id1` + +但最终真实 key 仍然必须是: + +```txt +user:{userId}:flux +config:{key} +lock:{domain}:{id} +``` + +## HTTP route 命名 + +### 资源命名原则 + +- 优先使用复数资源名 +- 从属资源优先挂在父资源下 +- 当前登录用户资源优先使用 `me` + +推荐: + +```txt +/api/users/me/flux +/api/users/me/flux/history +``` + +不推荐: + +```txt +/api/user/flux +/api/flux +``` + +### 版本化约束 + +如果某类 HTTP API 需要长期稳定对外契约,优先在一个明确子树下版本化,例如: + +```txt +/api/v1/... +/api/openai/v1/... +``` + +不要让“部分资源版本化、部分资源裸挂”长期并存而没有说明。 + +## TODO + +- 把 `apps/server` 中现有 Redis key / channel 继续向 helper 收口,避免业务代码里散落模板字符串。 +- 统一把旧式 key 命名迁移到分段命名风格,优先处理 Flux cache、chat broadcast、lock key。 +- 给 `configKV` 增补一份“哪些配置属于 infra、哪些属于运营策略”的清单,避免继续模糊放置位置。 +- 把所有 `configKV.getOptional(...) ?? defaultValue` 模式清理掉,默认值统一回到 `ConfigEntrySchemas`。 +- 评估是否把 `/api/v1` 收口为兼容层 API,并明确业务资源是否也需要统一版本化。 diff --git a/apps/server/docs/ai-context/observability-conventions.md b/apps/server/docs/ai-context/observability-conventions.md new file mode 100644 index 000000000..4a436ce56 --- /dev/null +++ b/apps/server/docs/ai-context/observability-conventions.md @@ -0,0 +1,159 @@ +# Observability Conventions + +这份约定定义 AIRI 服务端新增 trace / metric attributes 时应该遵守的命名规则,目标是减少自定义前缀扩散,并让 Grafana / Tempo / Loki 查询尽量对齐 OpenTelemetry 语义约定。 + +## 总原则 + +- 能直接映射到 OpenTelemetry semantic conventions 的字段,优先使用标准字段。 +- 不能映射到标准字段、但确实属于 AIRI 业务语义的字段,统一放到 `airi.*` 命名空间下。 +- 不要新增新的顶级前缀,例如 `llm.*`、`gateway.*`、`telegram.*` 之类的 attribute key。 +- span name、event name、metric name 不等于 attribute key;是否迁移它们要单独评估兼容性。 +- 代码里不要继续散落新的 observability key 字符串字面量;统一从 [packages/server-shared/src/observability.ts](/Users/luoling8192/Git/moeru-ai/airi/packages/server-shared/src/observability.ts) 引用。 + +## 标准字段优先级 + +### GenAI + +优先使用: + +- `GEN_AI_ATTR_OPERATION_NAME` +- `GEN_AI_ATTR_REQUEST_MODEL` +- `GEN_AI_ATTR_USAGE_INPUT_TOKENS` +- `GEN_AI_ATTR_USAGE_OUTPUT_TOKENS` +- `SERVER_ATTR_ADDRESS` +- `SERVER_ATTR_PORT` + +适用场景: + +- chat completion +- embeddings +- 其他能明确归类到 GenAI 上游调用的请求 + +注意: + +- 当前 OpenTelemetry GenAI semantic conventions 仍处于 `Development` 状态,因此只在“语义明确匹配”时采用。 +- 没有明确标准归属的字段不要硬塞进 `gen_ai.*`。 + +### Database / Redis + +优先使用: + +- `db.system.name` +- `db.operation.name` +- `db.namespace` +- `db.query.text` +- `db.response.status_code` +- `server.address` +- `server.port` + +Redis 相关优先复用 instrumentation 自动产生的标准属性,不要重复造一套并行命名。 + +## AIRI 自定义字段 + +以下场景使用 `airi.*`: + +- 计费或余额语义 +- 仅 AIRI 内部存在的流式控制字段 +- 临时调试但仍需要进入可观测系统的业务字段 + +当前示例: + +- `AIRI_ATTR_BILLING_FLUX_CONSUMED` +- `AIRI_ATTR_GEN_AI_STREAM` +- `AIRI_ATTR_GEN_AI_STREAM_INTERRUPTED` +- `AIRI_ATTR_GEN_AI_OPERATION_KIND` +- `AIRI_ATTR_GEN_AI_INPUT_MESSAGES` +- `AIRI_ATTR_GEN_AI_INPUT_TEXT` +- `AIRI_ATTR_GEN_AI_OUTPUT_TEXT` + +## Metric Name 策略 + +当前 `apps/server` 仍保留以下 metric name: + +- `llm.request.duration` +- `llm.request.count` +- `llm.tokens.prompt` +- `llm.tokens.completion` +- `flux.consumed` + +这是有意为之,不是遗漏。 + +原因: + +- metric name 改动比 attribute 改动更容易破坏现有 Prometheus 查询、Grafana 面板和告警。 +- 目前更高价值的是先统一 metric attributes,使查询维度稳定。 +- 如需迁移 metric name,应该走兼容迁移方案,而不是在普通功能改动里直接重命名。 + +## Grafana / Prometheus 查询策略 + +面板和告警查询优先依赖 metric labels,对齐我们已经统一的 attributes。 + +### GenAI 面板应该查什么 + +优先使用这些 Prometheus label: + +- `gen_ai_request_model` +- `gen_ai_operation_name` +- `airi_gen_ai_operation_kind` +- `http_response_status_code` + +说明: + +- Prometheus 暴露时会把 attribute key 里的 `.` 转成 `_`,所以 `gen_ai.request.model` 会变成 `gen_ai_request_model`。 +- `gen_ai_operation_name` 适合 chat、embeddings 这类有明确 semconv 的操作。 +- `airi_gen_ai_operation_kind` 适合当前没有明确 semconv 的 AIRI 自定义操作类型,例如 `tts`、`asr`。 + +### 不再新增使用的旧查询维度 + +新增 dashboard、录制规则、告警时,不要再新增依赖这些旧 label: + +- `model` +- `type` + +旧面板可以渐进迁移,不要求一次性全部替换,但新改动必须直接使用新标签。 + +### 当前已落地的 dashboard 例子 + +[apps/server/otel/grafana/dashboards/airi-server-overview-cloud.json](/Users/luoling8192/Git/moeru-ai/airi/apps/server/otel/grafana/dashboards/airi-server-overview-cloud.json) 已经按以下方式查询: + +- Request rate by model: `gen_ai_request_model` +- Request rate by operation: `gen_ai_operation_name` + `airi_gen_ai_operation_kind` +- Latency by model: `gen_ai_request_model` +- Flux consumed by model: `gen_ai_request_model` +- Token throughput by model: `gen_ai_request_model` + +如果未来新增本地 dashboard 或新的 cloud dashboard,默认按这一套 label 维度来。 + +## Span Name 策略 + +span name 目前允许保留业务可读格式,例如: + +- `llm.gateway.chat` +- `llm.gateway.tts` +- `llm.gateway.asr` + +原因: + +- span name 主要服务于人工浏览和局部检索。 +- 语义筛选应优先依赖 attributes,而不是依赖 span name 文本。 + +如果未来统一 span name,也应保证查询主要依赖 `gen_ai.*` / `db.*` / `airi.*` attributes。 + +## 修改前检查 + +新增 observability 字段前,先问自己: + +1. 这个字段能否映射到已有 OTel semconv? +2. 如果不能,它是否明确属于 AIRI 业务语义? +3. 如果属于 AIRI,是否应该挂到 `airi.*`,而不是新造顶级前缀? +4. 我改的是 attribute key 还是 metric name / span name? +5. 如果是 metric name,是否已经评估 Prometheus / Grafana / alerting 兼容性? +6. 如果要改 dashboard,我是否优先用了 `gen_ai_request_model`、`gen_ai_operation_name`、`airi_gen_ai_operation_kind`,而不是旧的 `model` / `type`? + +## 当前参考实现 + +- [packages/server-shared/src/observability.ts](/Users/luoling8192/Git/moeru-ai/airi/packages/server-shared/src/observability.ts) +- [apps/server/src/routes/v1completions.ts](/Users/luoling8192/Git/moeru-ai/airi/apps/server/src/routes/v1completions.ts) +- [apps/server/src/libs/otel.ts](/Users/luoling8192/Git/moeru-ai/airi/apps/server/src/libs/otel.ts) +- [services/telegram-bot/src/llm/actions.ts](/Users/luoling8192/Git/moeru-ai/airi/services/telegram-bot/src/llm/actions.ts) +- [services/telegram-bot/src/bots/telegram/agent/actions/read-message.ts](/Users/luoling8192/Git/moeru-ai/airi/services/telegram-bot/src/bots/telegram/agent/actions/read-message.ts) diff --git a/apps/server/docs/ai-context/redis-boundaries-and-pubsub.md b/apps/server/docs/ai-context/redis-boundaries-and-pubsub.md new file mode 100644 index 000000000..e22f3757b --- /dev/null +++ b/apps/server/docs/ai-context/redis-boundaries-and-pubsub.md @@ -0,0 +1,188 @@ +# Redis Boundaries And Pub/Sub + +## 目标 + +这篇文档约束服务端使用 Redis 时的几个高风险边界: + +- key / channel 拼接 +- Pub/Sub payload 序列化与反序列化 +- Redis 返回值的运行时校验 +- Redis 与 Postgres 的职责边界 + +这不是“推荐写法”集合,而是后续改代码时应该默认遵守的约束。 + +## 一句话规则 + +- 不要在业务代码里到处手写 Redis key / channel 模板字符串。 +- 不要把 TypeScript 类型注解当成 Redis 边界的运行时校验。 +- 不要把 Pub/Sub 当持久化通道。 +- 不要让 Redis 承担余额、账本、订单这类真相源职责。 + +## Redis 职责边界 + +当前 `apps/server` 中 Redis 主要承担四类职责: + +- cache + - 例如 `user:{userId}:flux` +- config KV + - 例如 `config:{key}` +- Pub/Sub + - 例如聊天跨实例广播 `chat:{userId}:broadcast` +- Streams + - 例如 `billing-events` + +其中: + +- Postgres 是余额、账本、订单、聊天消息等持久状态的唯一真相源 +- Redis Pub/Sub 只负责降低跨实例通知延迟,不提供持久化、回放、补偿 +- Redis Streams 用于异步事件消费,但也必须在边界层做输入输出校验 + +## Key / Channel 收口规则 + +### 必须收口 + +Redis key 和 Pub/Sub channel 必须通过单独 helper 构造,不要在多个调用点重复写模板字符串。 + +推荐模式: + +```ts +function fluxRedisKey(userId: string): string { + return `user:${userId}:flux` +} + +function userBroadcastChannel(userId: string): string { + if (typeof userId !== 'string' || userId.length === 0) { + throw new TypeError('user broadcast channel requires a non-empty string userId') + } + + return `chat:${userId}:broadcast` +} +``` + +这样做的原因不是“风格统一”,而是为了避免: + +- key 前缀分散在多个文件 +- 某个调用点把对象、空串、错误 id 拼进 channel +- 后续重构前缀或路由粒度时漏改 + +### 禁止依赖模板字符串兜底 + +不要假设 `` `${value}` `` 可以安全把任意值转成 Redis key。 + +原因: + +- 如果 `value` 在运行时是对象,会得到 `[object Object]` +- 这类错误不会在 TypeScript 编译期暴露 +- 一旦写进 Redis channel / key,排查成本很高 + +## Pub/Sub Payload 规则 + +### 发布侧 + +发布侧必须显式构造消息对象,不要把“业务对象刚好长得像 payload”当成协议。 + +推荐模式: + +```ts +interface BroadcastMessage { + userId: string + payload: { + chatId: string + messages: unknown[] + fromSeq: number + toSeq: number + } +} + +function createBroadcastMessage( + userId: string, + payload: BroadcastMessage['payload'], +): BroadcastMessage { + if (typeof userId !== 'string' || userId.length === 0) { + throw new TypeError('broadcast message requires a non-empty string userId') + } + + return { userId, payload } +} +``` + +### 消费侧 + +消费侧不要只写: + +```ts +const data = JSON.parse(message) as BroadcastMessage +``` + +因为这只是类型断言,不是校验。 + +至少要验证: + +- `userId` 是非空字符串 +- `payload.chatId` 是字符串 +- `payload.messages` 是数组 +- `fromSeq` / `toSeq` 是数字 + +如果消息不合法,应该记录错误并丢弃,而不是继续广播到本地连接。 + +## Streams 边界规则 + +Redis Streams 的参考实现已经在 `src/libs/mq/stream.ts` 里。 + +这层模式值得复用的点有两个: + +- 输入通过 `serialize()` 收口 +- 输出通过 `deserialize()` 和运行时检查收口 + +也就是说: + +- 不要在业务代码里裸写 `XADD` / `XREADGROUP` +- 不要相信 Redis 返回值一定符合你期望的 shape +- 边界校验失败时应该尽早抛错,而不是继续传播脏数据 + +## Chat WS 当前约束 + +`src/routes/chat-ws.ts` 当前采用: + +- 同实例内存连接表 +- 跨实例 Redis Pub/Sub + +这个设计的语义必须明确: + +- 广播通知不是持久化消息 +- 丢广播不会丢聊天真相数据 +- 客户端补齐消息仍然依赖 `pullMessages` + +因此后续如果改聊天同步: + +- 需要“可重放”时,不要继续堆在 Pub/Sub 上 +- 需要“跨实例即时通知”时,可以继续用 Pub/Sub +- 需要“持久事件消费”时,应优先考虑 Streams + +## 修改 Redis 代码时的检查清单 + +- 这个 Redis 数据是 cache、KV、Pub/Sub 还是 Stream? +- 它是不是被误当成真相源? +- key / channel 是否通过 helper 统一构造? +- 是否校验了关键标识符,例如 `userId`、`chatId`、`streamMessageId`? +- Pub/Sub payload 是否有显式创建函数和解析函数? +- 解析失败时是否会安全丢弃,而不是继续传播? +- 这个需求是否其实应该用 Streams,而不是 Pub/Sub? + +## 当前代码可直接参考的位置 + +- key helper + - `src/services/flux.ts` +- Streams 边界封装 + - `src/libs/mq/stream.ts` +- Pub/Sub 聊天广播 + - `src/routes/chat-ws.ts` +- 命名规范和待迁移事项 + - `config-and-naming-conventions.md` + +## 对 AI / 后续修改者的直接要求 + +- 新增 Redis key / channel 时,先写 helper,再写调用点 +- 新增 Pub/Sub payload 时,先定义消息 shape 和 parse / create 边界,再接业务逻辑 +- 如果看到业务代码里散落 `` `prefix:${id}` ``,优先做小范围收口 +- 如果看到 `JSON.parse(...) as SomeType` 出现在 Redis 边界,默认把它视为待修复点 diff --git a/apps/server/docs/ai-context/transport-and-routes.md b/apps/server/docs/ai-context/transport-and-routes.md index b485aa617..5b65770c5 100644 --- a/apps/server/docs/ai-context/transport-and-routes.md +++ b/apps/server/docs/ai-context/transport-and-routes.md @@ -10,7 +10,7 @@ - `/api/providers` - `/api/chats` - `/api/v1` -- `/api/flux` +- `/api/users/me/flux` - `/api/stripe` - `GET /ws/chat` @@ -135,6 +135,12 @@ - 同实例:内存 `Map>` - 跨实例:Redis Pub/Sub,channel 前缀 `chat:broadcast:` +实现约束: + +- Redis Pub/Sub 只承担通知职责,不承担持久化和重放职责 +- key / channel 与 payload 边界应集中收口,不要在调用点散落模板字符串和裸 `JSON.parse` +- 具体规范见 `redis-boundaries-and-pubsub.md` + ### `/api/v1` 实现位置: @@ -150,11 +156,8 @@ - `POST /api/v1/chat/completions` - `POST /api/v1/chat/completion` - -已实现但暂未挂载: - -- `handleTTS` -- `handleTranscription` +- `POST /api/v1/audio/speech` +- `POST /api/v1/audio/transcriptions` 请求流程: @@ -178,7 +181,7 @@ - 流结束后再 best-effort 扣费 - 扣费失败只打 error log,不回滚给客户端 -### `/api/flux` +### `/api/users/me/flux` 实现位置: @@ -189,9 +192,9 @@ 主要能力: -- `GET /api/flux` +- `GET /api/users/me/flux` - 读取当前用户余额 -- `GET /api/flux/history` +- `GET /api/users/me/flux/history` - 读取用户可见流水 ### `/api/stripe` diff --git a/apps/server/docs/ai-context/workers-and-runtime.md b/apps/server/docs/ai-context/workers-and-runtime.md index c9fb29603..31dc6c901 100644 --- a/apps/server/docs/ai-context/workers-and-runtime.md +++ b/apps/server/docs/ai-context/workers-and-runtime.md @@ -85,6 +85,8 @@ - 真正补齐消息还是靠 `pullMessages` - 广播只是为了降低拉取延迟,不代表存在旧式 `sync` 端点 +如果要改 Redis key / channel 构造、Pub/Sub payload 或 Streams 边界,先看 `redis-boundaries-and-pubsub.md`。 + ## OpenTelemetry 初始化在 `src/libs/otel.ts`。 diff --git a/apps/server/tsconfig.json b/apps/server/tsconfig.json index 9eb440a45..4c4e5f9a4 100644 --- a/apps/server/tsconfig.json +++ b/apps/server/tsconfig.json @@ -8,17 +8,8 @@ "ESNext" ], "useDefineForClassFields": true, - "baseUrl": ".", "module": "ESNext", "moduleResolution": "Bundler", - "paths": { - "@proj-airi/server-sdk-shared": [ - "../../packages/server-sdk-shared/src/index.ts" - ], - "@proj-airi/server-sdk-shared/*": [ - "../../packages/server-sdk-shared/src/*" - ] - }, "resolveJsonModule": true, "types": [ "vitest", @@ -29,7 +20,6 @@ "skipLibCheck": true }, "include": [ - "../../packages/server-sdk-shared/src/**/*.ts", "src/**/*.ts", "src/**/*.d.ts" ]