feat: 重构为 Monorepo 架构并实现 HTTP Server

架构变更:
- 采用 pnpm workspaces 实现 Monorepo 结构
- 将现有代码迁移到 packages/core
- 新增 packages/server HTTP 服务层

Server 功能:
- REST API: 会话管理、工具管理、配置管理
- WebSocket: 实时双向通信支持
- SSE: 服务端事件推送
- Hono + Bun 作为运行时

API 端点:
- GET/POST /api/sessions - 会话 CRUD
- GET/POST /api/sessions/:id/messages - 消息管理
- GET /api/sessions/:id/events - SSE 事件流
- WS /api/ws/:sessionId - WebSocket 连接
- GET/POST /api/tools - 工具管理
- GET/PUT /api/config - 配置管理
This commit is contained in:
2025-12-12 10:42:20 +08:00
parent 59dbed926e
commit 5e32375f0e
301 changed files with 3281 additions and 43 deletions
@@ -0,0 +1,99 @@
/**
* 图数据结构
* 用于 PageRank 算法
*/
import type { GraphEdge } from '../types.js';
export class Graph {
/** 邻接表:from -> edges[] */
private outEdges: Map<string, GraphEdge[]> = new Map();
/** 反向邻接表:to -> edges[] */
private inEdges: Map<string, GraphEdge[]> = new Map();
/** 所有节点 */
private nodes: Set<string> = new Set();
/**
* 添加边
*/
addEdge(edge: GraphEdge): void {
this.nodes.add(edge.from);
this.nodes.add(edge.to);
// 出边
if (!this.outEdges.has(edge.from)) {
this.outEdges.set(edge.from, []);
}
this.outEdges.get(edge.from)!.push(edge);
// 入边
if (!this.inEdges.has(edge.to)) {
this.inEdges.set(edge.to, []);
}
this.inEdges.get(edge.to)!.push(edge);
}
/**
* 获取所有节点
*/
getNodes(): string[] {
return Array.from(this.nodes);
}
/**
* 获取节点的出边
*/
getOutEdges(node: string): GraphEdge[] {
return this.outEdges.get(node) || [];
}
/**
* 获取节点的入边
*/
getInEdges(node: string): GraphEdge[] {
return this.inEdges.get(node) || [];
}
/**
* 获取节点的出度(考虑权重)
*/
getOutDegree(node: string): number {
const edges = this.outEdges.get(node) || [];
return edges.reduce((sum, e) => sum + e.weight, 0);
}
/**
* 获取节点的入度(考虑权重)
*/
getInDegree(node: string): number {
const edges = this.inEdges.get(node) || [];
return edges.reduce((sum, e) => sum + e.weight, 0);
}
/**
* 获取边数量
*/
getEdgeCount(): number {
let count = 0;
for (const edges of this.outEdges.values()) {
count += edges.length;
}
return count;
}
/**
* 清空图
*/
clear(): void {
this.outEdges.clear();
this.inEdges.clear();
this.nodes.clear();
}
/**
* 是否为空
*/
isEmpty(): boolean {
return this.nodes.size === 0;
}
}
@@ -0,0 +1,7 @@
/**
* 排序模块导出
*/
export { Graph } from './graph.js';
export { pagerank, distributeRanksToDefinitions } from './pagerank.js';
export type { PageRankOptions } from './pagerank.js';
@@ -0,0 +1,146 @@
/**
* PageRank 算法实现
* 基于 Aider 的实现,用于代码符号相关性排序
*/
import { Graph } from './graph.js';
export interface PageRankOptions {
/** 阻尼系数 (默认 0.85) */
damping?: number;
/** 最大迭代次数 (默认 100) */
iterations?: number;
/** 收敛阈值 (默认 1e-6) */
tolerance?: number;
/** 个性化向量:节点 -> 初始权重 */
personalization?: Map<string, number>;
}
/**
* PageRank 算法
*
* @param graph - 图结构
* @param options - 算法选项
* @returns 节点排名 Map<节点, 排名值>
*/
export function pagerank(
graph: Graph,
options: PageRankOptions = {}
): Map<string, number> {
const {
damping = 0.85,
iterations = 100,
tolerance = 1e-6,
personalization,
} = options;
const nodes = graph.getNodes();
const n = nodes.length;
if (n === 0) {
return new Map();
}
// 初始化排名
let ranks = new Map<string, number>();
const baseRank = 1 / n;
// 处理个性化向量
let persVector = new Map<string, number>();
if (personalization && personalization.size > 0) {
// 归一化个性化向量
const total = Array.from(personalization.values()).reduce((a, b) => a + b, 0);
if (total > 0) {
for (const [node, value] of personalization) {
persVector.set(node, value / total);
}
}
} else {
// 均匀分布
for (const node of nodes) {
persVector.set(node, baseRank);
}
}
// 初始排名 = 个性化向量
for (const node of nodes) {
ranks.set(node, persVector.get(node) || baseRank);
}
// 迭代计算
for (let iter = 0; iter < iterations; iter++) {
const newRanks = new Map<string, number>();
let diff = 0;
// 计算悬挂节点的贡献(没有出边的节点)
let danglingSum = 0;
for (const node of nodes) {
const outEdges = graph.getOutEdges(node);
if (outEdges.length === 0) {
danglingSum += ranks.get(node) || 0;
}
}
for (const node of nodes) {
// 基础分数:(1 - damping) * 个性化 + damping * 悬挂贡献
let rank =
(1 - damping) * (persVector.get(node) || baseRank) +
(damping * danglingSum) / n;
// 收集入边贡献
const inEdges = graph.getInEdges(node);
for (const edge of inEdges) {
const sourceRank = ranks.get(edge.from) || 0;
const outDegree = graph.getOutDegree(edge.from);
if (outDegree > 0) {
// 边权重占源节点总出度的比例
rank += damping * sourceRank * (edge.weight / outDegree);
}
}
newRanks.set(node, rank);
diff += Math.abs(rank - (ranks.get(node) || 0));
}
ranks = newRanks;
// 检查收敛
if (diff < tolerance) {
break;
}
}
return ranks;
}
/**
* 将 PageRank 排名分配到定义上
* 按照 Aider 的方式:将源节点的排名按边权重比例分配给目标定义
*
* @param graph - 图结构
* @param nodeRanks - 节点 PageRank 排名
* @returns 定义排名 Map<"file:ident", rank>
*/
export function distributeRanksToDefinitions(
graph: Graph,
nodeRanks: Map<string, number>
): Map<string, number> {
const definitionRanks = new Map<string, number>();
for (const src of graph.getNodes()) {
const srcRank = nodeRanks.get(src) || 0;
const outEdges = graph.getOutEdges(src);
const totalWeight = outEdges.reduce((sum, e) => sum + e.weight, 0);
if (totalWeight === 0) continue;
for (const edge of outEdges) {
const edgeRank = (srcRank * edge.weight) / totalWeight;
const key = `${edge.to}:${edge.ident}`;
definitionRanks.set(key, (definitionRanks.get(key) || 0) + edgeRank);
}
}
return definitionRanks;
}