【免费下载链接】rocketride-serverHigh-performance AI pipeline engine with a C core and 50 Python-extensible nodes. Build, debug, and scale LLM workflows with 13 model providers, 8 vector databases, and agent orchestration, all from your IDE. Includes VS Code extension, TypeScript/Python SDKs, and Docker deployment.项目地址https://gitcode.com/gh_mirrors/ro/rocketride-server点击查看免费下载本篇技术指南围绕 RocketRide 仓库中 TypeScript 客户端 SDK 的client.log运行日志 API见 docs/public/typescript/logs.md展开。它讲解一次任务一条连续 JSONL 事件流run-log continuum的存储模型、以openEventStream()为核心的 DVR 会话回放协议seed-then-stream、chapters/read/segment/delete四种读取与维护操作以及底层的rrext_logDAP 线与权限边界。读完你将掌握如何用几行代码复现任意历史运行的完整过程、如何将回放与实时监控无缝衔接、如何按需批量导出日志以及如何在团队部署场景中正确选择流的作用域scope。核心概念一次任务一条流一次运行一个章节在 RocketRide 的日志体系里没有每次运行一个日志文件这种模型。每一个任务身份identity都对应一条连续的事件流continuum即一个按行追加的 JSONL 事件流而每次运行只是这条流里的一个章节chapter / track。流的身份由如下元组唯一确定// 流身份LogStreamRef { projectId: string; // 项目 ID source: string; // 源任务ID teamId?: string; // 可选指向某个团队的 DEPLOY 连续流 runKind?: dev | deploy; // 可选无 teamId 时的个人作用域选择 }作用域scope规则在 类型定义 中有精确定义带teamId指向该团队的DEPLOY 连续流。部署运行以团队身份执行并写入团队日志树任何拥有 monitor 权限的队友都可以读取与回放参见 deploy.md 中Scheduled runs execute as the team…readable by teammates viaclient.logwithteamId。不带teamId指向你自己的流此时可选的runKind在两条个人连续流之间选择省略或dev你的开发dev流默认值deploy你的个人me部署流——这是teamId存在时无法表达的唯一种类deploy 属性但用户私有。// 你自己的 dev 流 const stream { projectId: proj-1, source: chat_1 }; // 若要读某个团队的部署连续流 const teamStream { projectId: proj-1, source: chat_1, teamId: team-prod };流由身份元组寻址永远不用任务 token。token 是凭据绝不进入日志系统。这条流具备以下持久性特性见原文档 logs.md跨断线、跨服务重启存活流写入由引擎持久化断线/重启不丢数据回放与实时同源过去运行的回放与实时监控走同一批面板与同一套 API保留策略retention环形缓冲ring约最近 1 GB叠加历史时长dev 流 7 天 / deploy 流 30 天。DVR 会话openEventStream()与 seed-then-stream 协议client.log.openEventStream(stream)在某个源连续流上打开一个DVR 会话实现类为LogEventStream见 log-stream.ts。会话让使用者只思考时间线上的位置position存储细节segments 分段、keyframes 关键帧、deltas 增量全部不可见会话交付的每个事件都是完全重建后的完整事件。协议seed-then-streamseek(pos)将会话定位到某个位置pos为 epoch 秒或字符串liveget*()系列调用以该位置时刻的状态填充你的面板console、status、tracesplay(pos, speed, cb)只流式投递严格晚于种子所覆盖范围的事件——无缝隙no gap、无重复no duplicate。关键语义源码注释 log-stream.ts 与文档一致速度speed0 尽可能快flat-out1 实时速度10 10 倍速自动追平auto-pin从过去位置回放时一旦追上墙钟wall clock会自动钉住实时live实时并不是一种独立模式它就是钉在 now 上的位置seek(live)。const stream { projectId: proj-1, source: chat_1 }; const session client.log.openEventStream(stream); // 标准启动流程定位、播种面板、然后滚动。 await session.seek(live); const status await session.getStatus(); // 该位置时刻的状态快照 const consoleLines await session.getConsole(500); // 该时刻 console 实际显示的内容 const traces await session.getTraces(50); // 所有在途 trace 最近关闭的 50 条 await session.play(undefined, 0, ({ event }) fold(event)); // 从某次过去运行的起点以 10 倍速回放。 const [first] await session.getChapters(); await session.play(first.beginTime, 10, ({ event }) fold(event)); // 深入某一条 trace调用树从恰好包含它的 segments 稀疏拉取。 const detail await session.getTrace(traces.closed[0].beginSeq); session.pause(); // 冻结位置 session.closeEventStream(); // 释放会话会话 API 的边界与细节getTraces(n)的窗口约束n 50时抛错MAX_TRACES 50常量与throw new Error(...)见 log-stream.ts 与 #L304-L311。会话暴露所有在途in-flighttrace外加最近关闭 trace 的滑动窗口更早的 trace 仍可通过 seek 到其生命周期内的某个位置来访问。getTrace(traceId)的身份契约trace 由它 begin 事件的continuum seq唯一标识——传入 trace 自身的beginSeq来自LogTraceSummary而不是章节的 beginSeq。原因slot ID 会被复用reuse而beginSeq永不复用。类型定义 types/log.ts 明确写道id只是显示用 ID永远把beginSeq或 begin 事件的 seq传给getTrace。在 log-stream.ts 的实现中解析流程是确定性的、与位置无关的先根据 span 表定位包含该 seq 的 segment找到 begin 事件再沿其 slot 向前收集到匹配的end跨 segment、必要时跨 live 尾部。当 seq 已低于保留水平线retention horizon或该 seq 不存在 trace-begin 事件slot 复用或 fold docId 不是 trace 身份时会失败。ingestLive(event)持有实时订阅的宿主host把到达的事件喂给会话钉在实时时到达节奏即投递节奏arrival paces delivery。实现上log-stream.ts无 body 时间戳的事件会被丢弃An event without body stamps is not a stamped task event且严格按 seq 去重seq 最后一个已缓存 seq的直接忽略。会话的监控订阅会话构造时会以流的 scope 为 key 注册[all]类型的 monitorlog-stream.tscloseEventStream()释放该注册——服务端按 key 引用计数可与宿主自己的注册共存这属于会话的内部行为不增加任何 wire 面。chapters()一次小读取拿到整条时间线client.log.chapters(stream)返回流的时间线一次调用即可获得时间线 UI 所需的全部元数据返回结构见 LogChaptersResult字段含义chapters每次运行的 begin/end 时间、起始 seq、结果outcomesegments活动 spansegment 时间范围供时间线活动条绘制startTime/endTime保留窗口的起点水平线/ 最新活动时间horizonSeqring/age 淘汰后仍保留的首个 seqcompleted当前是否没有运行在写这条流// 自己的 dev 流加 teamId: team-prod 可读某团队的部署连续流。 const stream { projectId: proj-1, source: chat_1 }; const timeline await client.log.chapters(stream); for (const track of timeline.chapters) { console.log(track.beginTime, track.endTime, track.outcome); }LogChapter的outcome取值为ok | error | cancelled运行进行中为 nullbeginSeq即该运行在连续流中的首条 seqtraceLevel记录该运行的 trace 级别null/none表示 trace 关闭见 types/log.ts。实现上log.ts它走rrext_log命令的chapters子命令SDK 层透传stream元组。read()有界分页的顺序事件读取client.log.read(stream, params)对连续流做范围化、分页化的事件读取。支持三种范围形式参数定义见 LogReadParamsseq 范围fromSeq/toSeq两端含时间范围fromTime/toTime省略toTime表示到当前时间到 segmentfromTimetoSegment读到并包含该 segment。分页参数maxEvents/maxBytes会被服务端钳制server-clamped到其上限当响应携带nextSeq时把它作为cursor传回即可继续翻页。types数组在服务端过滤事件类型例如[output]用于日志页。truncatedAtSeq字段出现时说明本次请求已经下探到保留水平线之下即最老的部分已被淘汰。let cursor: number | undefined; do { const page await client.log.read(stream, { fromSeq: 0, cursor, types: [output] }); for (const event of page.events) { process.stdout.write(String(event.body?.output ?? )); } cursor page.nextSeq; } while (cursor ! undefined);body 里的连续流时间戳——唯一的存在位置每个事件都在其body中携带连续流时间戳且只有 body 里有LogEventBodybody.eventTimeepoch 秒浮点引擎入口处只打一次戳stamped once at engine ingressbody.logSeq连续流序号目录catalog播种——全新流从 1 开始并在跨运行、跨重启后从记录的lastSeq 1继续严格单调递增。这两个时间戳在实时投递与回放时完全一致identical live and on replay。而 DAP 信封自身的seq只是每连接的协议记账号与连续流毫无关系。对旧 v2 段时间戳在 header的兼容由 normalizeStamps 在解码时统一搬进 body——消费方永远只读 body 里的戳。segment()按字节偏移拉取原始 JSONL 的批量回放路径client.log.segment(stream, segmentId, { offset, maxBytes })获取某个 segment 的原始 JSONL 字节按字节偏移分块chunk返回——这是批量回放bulk replay的路径。服务端不做行扫描、过滤或解析直接原样交出不可变的 segment 内容且每个响应块都是整行对齐的每个响应都结束在换行符上因此每个 chunk 都能独立解析。重复携带返回的nextOffset直到final: true为止。segment 表id 时间范围来自chapters()的结果。let offset 0; for (;;) { const chunk await client.log.segment(stream, 0, { offset }); for (const line of chunk.data.split(\n)) { if (line.trim()) handleEvent(JSON.parse(line)); } if (chunk.final) break; offset chunk.nextOffset!; }返回结构LogSegmentResult包括segment段 ID、offset本块起始字节、data原始 JSONL 文本、size段总字节数活动段会增长、nextOffset继续的偏移耗尽时为 null、final是否到达段尾。实现上log.ts就是rrext_log的segment子命令SDK 只是透传。选型建议来自原文档消费完整运行回放、导出时优先用segment()做过滤或窄范围查询时用read()。delete()破坏性维护操作client.log.delete(stream, options)是破坏性操作返回{ deletedSegments }beforeTime丢弃整段早于该截止时间的 segments章节会被裁剪、水平线前移all: true移除整条流包括其控制文件control file。// 删除一天前的全部日志段 await client.log.delete(stream, { beforeTime: Date.now() / 1000 - 86400 }); // 删除整条流 await client.log.delete(stream, { all: true });注意beforeTime的单位是epoch 秒Date.now() / 1000与eventTime的时间刻度一致不要误传毫秒。Wire 面与权限模型所有方法都走同一条rrext_logDAP 命令由subcommand参数分派chapters、read、segment、delete——LogApi的实现确认了这一点log.ts而openEventStream()是纯客户端组合它在底层发出chapters与segment调用并在会话打开期间注册实时 monitor 订阅closeEventStream()释放自身不增加任何新的 wire 面。权限规则如下操作所需权限chapters/read/segment读task.monitordelete写/破坏task.control作用域决定权限解析对象请求寻址的 scope 决定了这些权限在谁的流上解析——不带teamId访问的是你自己的 dev 流runKind: deploy则访问你的个人 me 部署流带teamId权限针对目标团队检查——团队成员身份本身就是读/写权限membership is the read/write right。会话的 monitor 注册同样遵循该 scope 规则monitorScope团队流订阅团队的 run无团队流订阅调用者自己的 runrunKind仅在没有teamId且为deploy时随 key 传递。源码级纵深客户端 DVR 的内部机制若想理解openEventStream()会话为什么能做到无缝隙、无重复的回放值得读一读 log-stream.ts 中的几个内部设计Live bucket实时桶到达的实时事件被保存在一个特殊的缓存段里其段 ID 为Number.MAX_SAFE_INTEGER排在所有服务端段之后因此有序遍历pump、trace 收集总能最后到达它正如它概念上就是最新的段L88-L95。有界性桶在超过LIVE_BUCKET_SPLIT_BYTES24 MiB 序列化字节L88时触发 split——对照新拉取的目录把服务端已被封存段覆盖的前缀丢弃这些事件现在可按真实段重新拉取保留的后缀最多一个活动段的量从而支持任意长的实时观察而不失控L522-L545。LRU 解码段缓存常驻解码段上限 6 个MAX_RESIDENT_SEGMENTS淘汰策略为最近最少使用——播放/播种/trace 展开触达的正是当前位置关心的段因此 recency 天然跟随指针被淘汰的段封存段不可变一次原始拉取即可重新物化L104、L690-L709。flat-out 批处理速度 0 时每投递 250 个事件FLAT_OUT_BATCH让出一次事件循环避免巨大的追补把宿主饿死L77、L611-L616。pump 世代epoch守卫seek()/play()递增 pump 世代使任何仍在等待 fetch 的旧投递循环自动退役防止陈旧回调交错进新位置L222-L246、L552-L618。段编解码codec段的存储编码属于内部管道SDK 不对外导出storage encoding is plumbing, never public SDK surface。每个 segment 是自包含容器以{type:keyframe}前导行开头含完整 status 快照、byPipe、openFrames、closedRecent、console 滚动区见 SegmentKeyframe内部事件可携带delta 体——status 增量以同一段内的上一个 status/keyframe 为基底trace leave 增量以其配对的 enter 为基底。SegmentDecoderlog-codec.ts是有状态解码器applyShallowDeltaL104-L131实现浅增量逆运算字典字段合并一层、__deleted__键删除。会话消费方永远只看到重建后的完整事件——这正是文档所述storage details invisible, every event fully reconstructed的实现基础。契约一致性这些类型与行为同时固化在 contract/versions/v1.3.d.ts 中LogStreamRef、LogChapter、LogEventStream类签名、LogApi四方法等是 SDK 与远端服务之间版本化契约的 TypeScript 镜像。实践小结要复现一次完整运行chapters()拿时间线 →segment()按整行分块批量拉取 → 自行逐行JSON.parse处理或直接开 DVR 会话seek(beginTime)play(pos, speed, cb)。要实时监控并随时可回溯openEventStream()seek(live)回放追平墙钟后自动进入实时不需要切换模式。要过滤式查询如只取 output用read()的types服务端过滤 cursor分页。要清理delete(stream, { beforeTime })裁剪老段delete(stream, { all: true })移除整条流记住delete需要task.control读操作需要task.monitor且teamId决定权限在哪个团队的流上解析。永远用身份元组projectId source 可选 teamId/runKind寻址绝不用任务 token排序、去重、追平永远依赖 body 里的eventTime与logSeq而不是 DAP 信封的seq。赞分享【免费下载链接】rocketride-serverHigh-performance AI pipeline engine with a C core and 50 Python-extensible nodes. Build, debug, and scale LLM workflows with 13 model providers, 8 vector databases, and agent orchestration, all from your IDE. Includes VS Code extension, TypeScript/Python SDKs, and Docker deployment.项目地址https://gitcode.com/gh_mirrors/ro/rocketride-server点击查看免费下载相关推荐RocketRide Run Logs 实战指南用 client.log 实现 DVR 式任务日志回放与监控RocketRide Run Logs 实战指南用 client.log 实现 DVR 式任务日志回放与监控 Run Logs 是 RocketRide 提供RocketRide 可观测性实战指南DVR 式运行日志、实时监控与事后故障复盘RocketRide 可观测性实战指南DVR 式运行日志、实时监控与事后故障复盘 RocketRide 把每一次管线运行都完整记录下来一条按管道身份 prRocketRide Observability 与追踪集成指南基于 DAP WebSocket 的实时事件订阅与 DVR 回放入门RocketRide Observability 与追踪集成指南基于 DAP WebSocket 的实时事件订阅与 DVR 回放入门 本篇技术指南围绕 Roc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
