Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
aa64e6f
feat(core): replace rooms with conversations
dingyi222666 Mar 21, 2026
5abb2b4
feat(core): refine conversation system, chain scheduler, and command …
dingyi222666 Mar 31, 2026
cf4dd05
feat(core): harden conversation lifecycle and routing
dingyi222666 Mar 31, 2026
ba47274
fix(core): move command helper out of auto imports
dingyi222666 Mar 31, 2026
a6621df
test(core): migrate conversation suite to mocha specs
dingyi222666 Mar 31, 2026
9a59e73
fix(core): harden conversation lifecycle and legacy cleanup
dingyi222666 Mar 31, 2026
a093687
fix: apply CodeRabbit auto-fixes
coderabbitai[bot] Mar 31, 2026
c6ede64
fix: apply CodeRabbit auto-fixes
coderabbitai[bot] Mar 31, 2026
6f1dd0b
fix(core): enforce target binding chat constraints
dingyi222666 Mar 31, 2026
7ad69bd
Update packages/core/src/services/conversation.ts
dingyi222666 Mar 31, 2026
461c88c
chore: normalize source file endings
dingyi222666 Mar 31, 2026
10b0f62
fix(adapter-dify): preserve shared Dify conversations
dingyi222666 Apr 1, 2026
4937217
refactor(core): remove auth quota system
dingyi222666 Apr 1, 2026
b799832
feat(core): rework conversation route rules
dingyi222666 Apr 1, 2026
5d84cd8
fix: apply CodeRabbit auto-fixes
coderabbitai[bot] Apr 1, 2026
84eae57
fix(extension-agent): add source and toolMask to runtime config
dingyi222666 Apr 1, 2026
ccaf54c
fix(core): resolve conversations across preset lanes
dingyi222666 Apr 1, 2026
e50eddb
fix(core): tighten preset lane conversation resolution
dingyi222666 Apr 2, 2026
ac6a756
fix(core): keep route-family conversation state consistent
dingyi222666 Apr 3, 2026
e1b3662
fix(core): secure conversation restore and rollback targets
dingyi222666 Apr 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"build": "yarn process-dynamic-import --lint && yarn fast-build shared-prompt-renderer && yarn fast-build core && yarn fast-build",
"bump": "yakumo version",
"dep": "yakumo upgrade",
"test": "cross-env TS_NODE_PROJECT=packages/core/tests/tsconfig.json yakumo mocha -r tsconfig-paths/register -r esbuild-register -r yml-register --exit",
"pub": "yakumo publish --tag latest",
"pub:next": "yakumo publish --tag next",
"lint": "yarn eslint packages --cache --ext=ts",
Expand All @@ -23,16 +24,20 @@
"@koishijs/cache": "^2.1.0",
"@koishijs/censor": "^1.1.0",
"@koishijs/client": "^5.30.9",
"@koishijs/plugin-database-memory": "^3.7.0",
"@koishijs/plugin-hmr": "^1.2.9",
"@koishijs/scripts": "^4.6.1",
"@types/chai": "^4.3.20",
"@types/he": "^1.2.3",
"@types/js-yaml": "^4.0.9",
"@types/marked": "^6.0.0",
"@types/mocha": "^10.0.10",
"@types/node": "^22.18.7",
"@types/qrcode": "^1.5.5",
"@typescript-eslint/eslint-plugin": "^7.18.1-alpha.3",
"@typescript-eslint/parser": "^8.45.1-alpha.0",
"atsc": "^2.1.0",
"chai": "^4.4.1",
"cross-env": "^7.0.3",
"esbuild": "^0.25.10",
"esbuild-register": "npm:@shigma/esbuild-register@^1.1.1",
Expand All @@ -57,18 +62,20 @@
"marked": "^15.0.12",
"marked-highlight": "^2.2.2",
"marked-katex-extension": "^5.1.5",
"mocha": "^9.2.2",
"prettier": "^3.6.2",
"qrcode": "^1.5.4",
"socks-proxy-agent": "^8.0.5",
"tsconfig-paths": "^4.2.0",
"tsx": "^4.20.6",
"typescript": "^5.9.2",
"undici": "^6.21.3",
"user-agents": "^2.0.0-alpha.679",
"ws": "^8.18.3",
"yakumo": "^1.0.0",
"yakumo-esbuild": "^1.0.0",
"yakumo-mocha": "^1.0.0",
"yakumo-tsc": "^1.0.0",
"yakumo": "^1.0.0-beta.20",
"yakumo-esbuild": "^1.0.0-beta.7",
"yakumo-mocha": "^1.0.0-beta.2",
"yakumo-tsc": "^1.0.0-beta.5",
"yml-register": "^1.2.5",
"zod": "3.25.76",
"zod-to-json-schema": "^3.24.6"
Expand Down
114 changes: 78 additions & 36 deletions packages/adapter-dify/src/requester.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export class DifyRequester extends ModelRequester<DifyClientConfig> {
conversationId,
config
)
const difyUser = this.resolveDifyUser(params)

let iter: ReturnType<typeof this._agentStream>

Expand All @@ -88,10 +89,11 @@ export class DifyRequester extends ModelRequester<DifyClientConfig> {
difyConversationId,
params.input[params.input.length - 1].content as string,
conversationId,
config
config,
difyUser
)
} else {
iter = this._workflowStream(params, config)
iter = this._workflowStream(params, conversationId, config)
}

for await (const chunk of iter) {
Expand All @@ -104,12 +106,9 @@ export class DifyRequester extends ModelRequester<DifyClientConfig> {
): string | undefined {
for (const message of messages) {
const conversationId =
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(message as any)?.additional_kwargs?.chatluna_conversation_id ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(message as any)?.additional_kwargs?.conversationId ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(message as any)?.additional_kwargs?.conversation_id
message?.additional_kwargs?.chatluna_conversation_id ||
message?.additional_kwargs?.conversationId ||
message?.additional_kwargs?.conversation_id

if (
typeof conversationId === 'string' &&
Expand All @@ -126,11 +125,11 @@ export class DifyRequester extends ModelRequester<DifyClientConfig> {
difyConversationId: string,
input: string,
conversationId: string,
config: { apiKey: string; workflowName: string; workflowType: string }
config: { apiKey: string; workflowName: string; workflowType: string },
difyUser: string
): AsyncGenerator<ChatGenerationChunk> {
const lastMessage = params.input?.[params.input.length - 1]
const query = getMessageContent(lastMessage?.content ?? input ?? '')
const difyUser = this.resolveDifyUser(params)
const { files, chatlunaMultimodal } = await this.prepareFiles(
params,
lastMessage,
Expand All @@ -148,7 +147,12 @@ export class DifyRequester extends ModelRequester<DifyClientConfig> {
} = {
query,
response_mode: 'streaming',
inputs: this.buildInputs(params, lastMessage, chatlunaMultimodal),
inputs: this.buildInputs(
params,
conversationId,
lastMessage,
chatlunaMultimodal
),
user: difyUser,
conversation_id:
difyConversationId == null ? '' : difyConversationId
Expand Down Expand Up @@ -220,7 +224,8 @@ export class DifyRequester extends ModelRequester<DifyClientConfig> {
await this.updateDifyConversationId(
conversationId,
config.workflowName,
updatedDifyConversationId
updatedDifyConversationId,
difyUser
)
break
}
Expand All @@ -229,6 +234,7 @@ export class DifyRequester extends ModelRequester<DifyClientConfig> {

private async *_workflowStream(
params: ModelRequestParams,
conversationId: string | undefined,
config: { apiKey: string; workflowName: string; workflowType: string }
): AsyncGenerator<ChatGenerationChunk> {
const lastMessage = params.input[params.input.length - 1] as
Expand All @@ -249,7 +255,12 @@ export class DifyRequester extends ModelRequester<DifyClientConfig> {
files?: InputFileObject[]
} = {
response_mode: 'streaming',
inputs: this.buildInputs(params, lastMessage, chatlunaMultimodal),
inputs: this.buildInputs(
params,
conversationId,
lastMessage,
chatlunaMultimodal
),
user: difyUser
}

Expand Down Expand Up @@ -286,7 +297,7 @@ export class DifyRequester extends ModelRequester<DifyClientConfig> {
throw new ChatLunaError(
ChatLunaErrorCode.API_REQUEST_FAILED,
new Error(
'error when calling qwen completion, Result: ' + chunk
'error when calling dify completion, Result: ' + chunk
)
)
}
Expand Down Expand Up @@ -316,13 +327,14 @@ export class DifyRequester extends ModelRequester<DifyClientConfig> {

private buildInputs(
params: ModelRequestParams,
conversationId: string | undefined,
lastMessage?: BaseMessage,
chatlunaMultimodal?: string
): Record<string, unknown> {
const inputs = {
input: getMessageContent(lastMessage?.content ?? ''),
chatluna_history: this.buildChatlunaHistory(params.input ?? []),
chatluna_conversation_id: params.id,
chatluna_conversation_id: conversationId,
...Object.keys(params.variables ?? {}).reduce((acc, key) => {
acc[`chatluna_${key}`] = params.variables?.[key]
return acc
Expand Down Expand Up @@ -397,7 +409,7 @@ export class DifyRequester extends ModelRequester<DifyClientConfig> {
}

private resolveDifyUser(params: ModelRequestParams): string {
if (this.ctx.chatluna.config.autoCreateRoomFromUser === true) {
if (this.ctx.chatluna.config.defaultGroupRouteMode === 'personal') {
return (
(params.variables?.['user_id'] as string) ||
(params.variables?.['user'] as string) ||
Expand Down Expand Up @@ -647,21 +659,35 @@ export class DifyRequester extends ModelRequester<DifyClientConfig> {
conversationId: string,
config: { apiKey: string; workflowName: string; workflowType: string }
) {
return this.ctx.chatluna.cache.get(
const cached = await this.ctx.chatluna.cache.get(
'chatluna/keys',
'dify/' + conversationId + '/' + config.workflowName
)

if (cached == null) {
return undefined
}

try {
return (JSON.parse(cached) as { id?: string }).id
} catch {
return cached
}
}

private async updateDifyConversationId(
conversationId: string,
workflowName: string,
difyConversationId: string
difyConversationId: string,
user: string
) {
return this.ctx.chatluna.cache.set(
'chatluna/keys',
'dify/' + conversationId + '/' + workflowName,
difyConversationId
JSON.stringify({
id: difyConversationId,
user
})
)
}

Expand Down Expand Up @@ -716,32 +742,48 @@ export class DifyRequester extends ModelRequester<DifyClientConfig> {
}
const conversationId = id
const config = this._config.value.additionalModel.get(model)
if (config == null || config.workflowName == null) {
this.ctx.logger.warn(
`Dify clear: config not found for model ${model}`
)
return
}
const cacheKey = 'dify/' + conversationId + '/' + config.workflowName
const cached = await this.ctx.chatluna.cache.get(
'chatluna/keys',
cacheKey
)
const difyConversationId = await this.getDifyConversationId(
conversationId,
config
)
let difyUser = 'chatluna'

if (cached != null) {
try {
difyUser =
(JSON.parse(cached) as { user?: string }).user ?? 'chatluna'
} catch {}
}

if (difyConversationId) {
await this._plugin
.fetch(this.concatUrl('/conversations/' + difyConversationId), {
const res = await this._plugin.fetch(
this.concatUrl('/conversations/' + difyConversationId),
{
headers: this._buildHeaders(config.apiKey),
method: 'DELETE',
body: JSON.stringify({ user: 'chatluna' })
})
.then(async (res) => {
if (res.ok) {
this.ctx.logger.info('Dify clear: success')
} else {
this.ctx.logger.warn(
'Dify clear: failed: ' + (await res.text())
)
}
})

await this.ctx.chatluna.cache.delete(
'chatluna/keys',
'dify/' + conversationId + '/' + config.workflowName
body: JSON.stringify({ user: difyUser })
}
)

if (res.ok) {
this.ctx.logger.info('Dify clear: success')
await this.ctx.chatluna.cache.delete('chatluna/keys', cacheKey)
} else {
this.ctx.logger.warn(
'Dify clear: failed: ' + (await res.text())
)
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions packages/adapter-gemini/src/requester.ts
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,8 @@ export class GeminiRequester
inputTokens: parsedChunk.usage.promptTokens,
outputTokens: parsedChunk.usage.completionTokens,
totalTokens: parsedChunk.usage.totalTokens,
inputImageTokens: parsedChunk.usage.inputImageTokens,
outputImageTokens: parsedChunk.usage.outputImageTokens,
inputAudioTokens: parsedChunk.usage.inputAudioTokens,
outputAudioTokens: parsedChunk.usage.outputAudioTokens,
cacheReadTokens: parsedChunk.usage.cacheReadTokens,
Expand Down
2 changes: 2 additions & 0 deletions packages/adapter-gemini/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export type ChatUsageMetadataPart = {
completionTokens: number
totalTokens: number
inputAudioTokens?: number
inputImageTokens?: number
outputImageTokens?: number
outputAudioTokens?: number
cacheReadTokens?: number
reasoningTokens?: number
Expand Down
5 changes: 5 additions & 0 deletions packages/adapter-gemini/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -962,6 +962,11 @@ export function getUsage(data: ChatResponse) {
completionTokens: getCompletionTokens(data),
totalTokens: usage.totalTokenCount,
inputAudioTokens: getModalityTokens(usage.promptTokensDetails, 'AUDIO'),
inputImageTokens: getModalityTokens(usage.promptTokensDetails, 'IMAGE'),
outputImageTokens: getModalityTokens(
usage.candidatesTokensDetails,
'IMAGE'
),
outputAudioTokens: getModalityTokens(
usage.candidatesTokensDetails,
'AUDIO'
Expand Down
15 changes: 13 additions & 2 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,8 @@
"url": "https://github.com/ChatLunaLab/chatluna/issues"
},
"scripts": {
"build": "atsc -b"
"build": "atsc -b",
"test": "yarn exec cross-env TS_NODE_PROJECT=tests/tsconfig.json mocha -r tsconfig-paths/register -r esbuild-register -r yml-register --exit \"tests/**/*.spec.ts\""
},
"engines": {
"node": ">=18.0.0"
Expand Down Expand Up @@ -265,13 +266,23 @@
"@koishijs/cache": "^2.1.0",
"@koishijs/censor": "^1.1.0",
"@koishijs/plugin-adapter-qq": "^4.10.1",
"@koishijs/plugin-database-memory": "^3.7.0",
"@koishijs/plugin-notifier": "^1.2.1",
"@types/chai": "^4.3.20",
"@types/he": "^1.2.3",
"@types/js-yaml": "^4.0.9",
"@types/mocha": "^10.0.10",
"@types/qrcode": "^1.5.5",
"@types/useragent": "^2",
"atsc": "^2.1.0",
"koishi-plugin-adapter-onebot": "^6.8.0"
"chai": "^4.4.1",
"cross-env": "^7.0.3",
"esbuild": "^0.25.10",
"esbuild-register": "npm:@shigma/esbuild-register@^1.1.1",
"koishi-plugin-adapter-onebot": "^6.8.0",
"mocha": "^9.2.2",
"tsconfig-paths": "^4.2.0",
"yml-register": "^1.2.5"
},
"peerDependencies": {
"koishi": "^4.18.9",
Expand Down
Loading
Loading