@gobing-ai/knowledge-kit 0.0.37 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/dist/board/collect/index.css +654 -16
- package/dist/board/collect/index.js +191 -64
- package/dist/board/daily-voice/index.css +679 -12
- package/dist/board/daily-voice/index.js +185 -66
- package/dist/board/itc/index.css +679 -13
- package/dist/board/itc/index.js +188 -66
- package/dist/index.js +2864 -139
- package/package.json +3 -2
- package/plugins/generations/content-gen/dist/index.js +158 -12
- package/plugins/generations/core-facts-gen/dist/index.js +158 -12
- package/plugins/generations/daily-article-gen/dist/index.js +158 -12
- package/plugins/generations/dailynews-gen/dist/index.js +158 -12
- package/plugins/generations/episode-plan-gen/dist/index.js +158 -12
- package/plugins/generations/gemini-tts/README.md +4 -3
- package/plugins/generations/gemini-tts/dist/index.js +158 -12
- package/plugins/generations/image-gen/dist/index.js +158 -12
- package/plugins/generations/news-report-gen/dist/index.js +158 -12
- package/plugins/generations/omni-voice-gen/README.md +15 -4
- package/plugins/generations/omni-voice-gen/profiles.json +1 -2
- package/plugins/generations/voice-gen/dist/index.js +158 -12
- package/plugins/generations/voxcpm-tts/README.md +21 -1
- package/plugins/generations/voxcpm-tts/dist/index.js +173 -13
- package/plugins/generations/voxcpm-tts/scripts/audit-speaker-consistency.py +109 -0
- package/plugins/generations/voxcpm-tts/src/voxcpm-client.ts +28 -1
- package/plugins/ingestions/aihot-ingest/dist/index.js +158 -12
- package/plugins/ingestions/horizon-ingest/dist/index.js +158 -12
- package/plugins/ingestions/last30days-ingest/dist/index.js +158 -12
- package/plugins/ingestions/web-search/dist/index.js +158 -12
- package/plugins/kk/commands/workflow-run.md +56 -18
- package/plugins/kk/decisions/kk-itc.yaml +46 -0
- package/plugins/kk/decisions/kk-seo-keyword.yaml +57 -0
- package/plugins/kk/decisions/kk-seo-page.yaml +230 -0
- package/plugins/kk/decisions/kk-seo-pair.yaml +21 -0
- package/plugins/kk/decisions/kk-seo-site.yaml +90 -0
- package/plugins/kk/plugin.json +1 -1
- package/plugins/kk/publish-presets/chinese-article.json +14 -0
- package/plugins/kk/publish-presets/technical-article.json +14 -0
- package/plugins/kk/references/smart-seo-attribution.md +85 -0
- package/plugins/kk/scripts/daily-stages.ts +42 -2
- package/plugins/kk/scripts/itc-stages.ts +25 -118
- package/plugins/kk/scripts/kk-workflow-stages.ts +38 -1
- package/plugins/kk/scripts/seo-decisions.ts +491 -0
- package/plugins/kk/scripts/seo-enrich.ts +728 -0
- package/plugins/kk/scripts/seo-facts.ts +610 -0
- package/plugins/kk/scripts/seo-fetch.ts +766 -0
- package/plugins/kk/scripts/seo-report.ts +460 -0
- package/plugins/kk/scripts/seo-rules.ts +1882 -0
- package/plugins/kk/scripts/seo-stages.ts +1044 -0
- package/plugins/kk/skills/article-adapt/SKILL.md +2 -2
- package/plugins/kk/skills/smart-seo/SKILL.md +183 -0
- package/plugins/kk/skills/smart-seo/references/method.md +126 -0
- package/plugins/kk/skills/smart-seo/references/narrative.md +125 -0
- package/plugins/kk/skills/smart-seo/references/operations.md +209 -0
- package/plugins/kk/skills/smart-seo/references/seo-geo-sources.md +90 -0
- package/plugins/kk/workflows/feature-verification.yaml +28 -24
- package/plugins/kk/workflows/kk-daily-ai-voice2.yaml +85 -17
- package/plugins/kk/workflows/kk-itc-author.yaml +406 -0
- package/plugins/kk/workflows/kk-itc.yaml +208 -682
- package/plugins/kk/workflows/kk-smart-seo.yaml +251 -0
- package/plugins/publishings/bilibili-pub/dist/index.js +21782 -0
- package/plugins/publishings/bilibili-pub/dist/profile-check.js +20410 -0
- package/plugins/publishings/bilibili-pub/package.json +20 -0
- package/plugins/publishings/bilibili-pub/plugin.json +12 -0
- package/plugins/publishings/bilibili-pub/src/flow.ts +1296 -0
- package/plugins/publishings/bilibili-pub/src/index.ts +139 -0
- package/plugins/publishings/bilibili-pub/src/profile-check.ts +19 -0
- package/plugins/publishings/bilibili-pub/src/selectors.ts +222 -0
- package/plugins/publishings/bilibili-pub/tsconfig.json +4 -0
- package/plugins/publishings/cnblogs-pub/dist/index.js +21383 -0
- package/plugins/publishings/cnblogs-pub/dist/profile-check.js +20412 -0
- package/plugins/publishings/cnblogs-pub/package.json +19 -0
- package/plugins/publishings/cnblogs-pub/plugin.json +12 -0
- package/plugins/publishings/cnblogs-pub/src/flow.ts +708 -0
- package/plugins/publishings/cnblogs-pub/src/index.ts +172 -0
- package/plugins/publishings/cnblogs-pub/src/profile-check.ts +24 -0
- package/plugins/publishings/cnblogs-pub/src/selectors.ts +104 -0
- package/plugins/publishings/cnblogs-pub/tsconfig.json +4 -0
- package/plugins/publishings/csdn-pub/dist/index.js +21552 -0
- package/plugins/publishings/csdn-pub/dist/profile-check.js +20963 -0
- package/plugins/publishings/csdn-pub/package.json +19 -0
- package/plugins/publishings/csdn-pub/plugin.json +12 -0
- package/plugins/publishings/csdn-pub/src/index.ts +1178 -0
- package/plugins/publishings/csdn-pub/src/profile-check.ts +23 -0
- package/plugins/publishings/csdn-pub/tsconfig.json +4 -0
- package/plugins/publishings/emdash-pub/dist/index.js +158 -12
- package/plugins/publishings/emdash-pub/plugin.json +22 -1
- package/plugins/publishings/infoq-pub/dist/index.js +160 -12
- package/plugins/publishings/infoq-pub/dist/profile-check.js +160 -12
- package/plugins/publishings/infoq-pub/plugin.json +21 -0
- package/plugins/publishings/jike-pub/dist/index.js +21379 -0
- package/plugins/publishings/jike-pub/dist/profile-check.js +20951 -0
- package/plugins/publishings/jike-pub/package.json +19 -0
- package/plugins/publishings/jike-pub/plugin.json +12 -0
- package/plugins/publishings/jike-pub/src/index.ts +800 -0
- package/plugins/publishings/jike-pub/src/profile-check.ts +21 -0
- package/plugins/publishings/jike-pub/tsconfig.json +4 -0
- package/plugins/publishings/juejin-pub/dist/index.js +160 -12
- package/plugins/publishings/juejin-pub/dist/profile-check.js +160 -12
- package/plugins/publishings/juejin-pub/plugin.json +21 -0
- package/plugins/publishings/medium-pub/dist/index.js +158 -12
- package/plugins/publishings/medium-pub/plugin.json +22 -1
- package/plugins/publishings/podcast-pub/dist/index.js +158 -12
- package/plugins/publishings/qiita-pub/dist/index.js +158 -12
- package/plugins/publishings/qiita-pub/plugin.json +24 -1
- package/plugins/publishings/shipinhao-pub/dist/index.js +21715 -0
- package/plugins/publishings/shipinhao-pub/dist/profile-check.js +20431 -0
- package/plugins/publishings/shipinhao-pub/package.json +19 -0
- package/plugins/publishings/shipinhao-pub/plugin.json +12 -0
- package/plugins/publishings/shipinhao-pub/src/flow.ts +1323 -0
- package/plugins/publishings/shipinhao-pub/src/index.ts +173 -0
- package/plugins/publishings/shipinhao-pub/src/profile-check.ts +25 -0
- package/plugins/publishings/shipinhao-pub/src/selectors.ts +241 -0
- package/plugins/publishings/shipinhao-pub/tsconfig.json +4 -0
- package/plugins/publishings/sspai-pub/dist/index.js +21371 -0
- package/plugins/publishings/sspai-pub/dist/profile-check.js +20414 -0
- package/plugins/publishings/sspai-pub/package.json +19 -0
- package/plugins/publishings/sspai-pub/plugin.json +12 -0
- package/plugins/publishings/sspai-pub/src/flow.ts +624 -0
- package/plugins/publishings/sspai-pub/src/index.ts +167 -0
- package/plugins/publishings/sspai-pub/src/profile-check.ts +25 -0
- package/plugins/publishings/sspai-pub/src/selectors.ts +122 -0
- package/plugins/publishings/sspai-pub/tsconfig.json +4 -0
- package/plugins/publishings/substack-pub/dist/index.js +160 -12
- package/plugins/publishings/substack-pub/dist/profile-check.js +160 -12
- package/plugins/publishings/substack-pub/plugin.json +21 -0
- package/plugins/publishings/surfdash-pub/dist/index.js +158 -12
- package/plugins/publishings/surfdash-pub/plugin.json +22 -1
- package/plugins/publishings/threads-pub/dist/index.js +158 -12
- package/plugins/publishings/wechatmp-pub/dist/index.js +1669 -502
- package/plugins/publishings/wechatmp-pub/dist/profile-check.js +204 -21
- package/plugins/publishings/wechatmp-pub/plugin.json +32 -0
- package/plugins/publishings/wechatmp-pub/src/cgi-draft.ts +540 -0
- package/plugins/publishings/wechatmp-pub/src/cgi-media.ts +380 -0
- package/plugins/publishings/wechatmp-pub/src/cgi.ts +485 -0
- package/plugins/publishings/wechatmp-pub/src/flow.ts +104 -0
- package/plugins/publishings/weibo-pub/dist/index.js +160 -12
- package/plugins/publishings/weibo-pub/dist/profile-check.js +160 -12
- package/plugins/publishings/weibo-pub/plugin.json +35 -0
- package/plugins/publishings/x-pub/dist/index.js +160 -12
- package/plugins/publishings/x-pub/dist/profile-check.js +160 -12
- package/plugins/publishings/x-pub/plugin.json +40 -0
- package/plugins/publishings/xhs-pub/dist/index.js +160 -12
- package/plugins/publishings/xhs-pub/dist/profile-check.js +160 -12
- package/plugins/publishings/xhs-pub/plugin.json +37 -0
- package/plugins/publishings/xiaoyuzhou-pub/dist/index.js +21491 -0
- package/plugins/publishings/xiaoyuzhou-pub/dist/profile-check.js +20417 -0
- package/plugins/publishings/xiaoyuzhou-pub/package.json +19 -0
- package/plugins/publishings/xiaoyuzhou-pub/plugin.json +12 -0
- package/plugins/publishings/xiaoyuzhou-pub/src/flow.ts +826 -0
- package/plugins/publishings/xiaoyuzhou-pub/src/index.ts +174 -0
- package/plugins/publishings/xiaoyuzhou-pub/src/profile-check.ts +26 -0
- package/plugins/publishings/xiaoyuzhou-pub/src/selectors.ts +125 -0
- package/plugins/publishings/xiaoyuzhou-pub/tsconfig.json +4 -0
- package/plugins/publishings/zenn-pub/dist/index.js +158 -12
- package/plugins/publishings/zenn-pub/plugin.json +22 -1
- package/plugins/generations/omni-voice-gen/voices/robin-news.wav +0 -0
|
@@ -11987,6 +11987,83 @@ import { parseArgs } from "util";
|
|
|
11987
11987
|
// ../../plugins/generations/voice-gen/src/index.ts
|
|
11988
11988
|
import { basename, dirname as dirname2, extname, join as join2, resolve } from "path";
|
|
11989
11989
|
|
|
11990
|
+
// ../../packages/kk-core/src/content-limits.ts
|
|
11991
|
+
var PLATFORM_CONTENT_LIMITS = Object.freeze({
|
|
11992
|
+
schemaVersion: 5,
|
|
11993
|
+
tagLimits: Object.freeze({
|
|
11994
|
+
default: 7,
|
|
11995
|
+
imageText: Object.freeze({
|
|
11996
|
+
instagram: 5,
|
|
11997
|
+
douyin: 5,
|
|
11998
|
+
kuaishou: 4,
|
|
11999
|
+
weixin: 1,
|
|
12000
|
+
xiaohongshu: 10
|
|
12001
|
+
}),
|
|
12002
|
+
shortVideo: Object.freeze({
|
|
12003
|
+
douyin: 5,
|
|
12004
|
+
kuaishou: 4,
|
|
12005
|
+
xiaohongshu: 10,
|
|
12006
|
+
bilibili: 10,
|
|
12007
|
+
zhihu: 5,
|
|
12008
|
+
tiktok: 20,
|
|
12009
|
+
youtube: 60
|
|
12010
|
+
})
|
|
12011
|
+
}),
|
|
12012
|
+
titleCapabilities: Object.freeze({
|
|
12013
|
+
dynamic: Object.freeze({
|
|
12014
|
+
bilibili: Object.freeze({ max: 20, unit: "utf16_code_units" }),
|
|
12015
|
+
zhihu: Object.freeze({ max: 50, unit: "utf16_code_units" }),
|
|
12016
|
+
douban: Object.freeze({ max: 70, unit: "utf16_code_units" }),
|
|
12017
|
+
x: null,
|
|
12018
|
+
threads: null,
|
|
12019
|
+
weibo: null,
|
|
12020
|
+
jike: null,
|
|
12021
|
+
zsxq: null,
|
|
12022
|
+
toutiao: null,
|
|
12023
|
+
juejin: null
|
|
12024
|
+
}),
|
|
12025
|
+
imageText: Object.freeze({
|
|
12026
|
+
xiaohongshu: Object.freeze({ max: 20, unit: "xiaohongshu_title_weighted" }),
|
|
12027
|
+
weixin: Object.freeze({ max: 20, unit: "utf16_code_units" }),
|
|
12028
|
+
douyin: Object.freeze({ max: 20, unit: "utf16_code_units" }),
|
|
12029
|
+
shipinhao: Object.freeze({ max: 22, unit: "utf16_code_units" }),
|
|
12030
|
+
reddit: Object.freeze({ max: 300, unit: "utf16_code_units" })
|
|
12031
|
+
}),
|
|
12032
|
+
article: Object.freeze({
|
|
12033
|
+
juejin: Object.freeze({ max: 100, unit: "utf16_code_units" }),
|
|
12034
|
+
csdn: Object.freeze({ max: 100, unit: "utf16_code_units" }),
|
|
12035
|
+
cnblogs: Object.freeze({ max: 128, unit: "utf16_code_units" }),
|
|
12036
|
+
weibo: Object.freeze({ max: 30, unit: "utf16_code_units" }),
|
|
12037
|
+
sspai: Object.freeze({ max: 80, unit: "utf16_code_units" })
|
|
12038
|
+
}),
|
|
12039
|
+
shortVideo: Object.freeze({
|
|
12040
|
+
shipinhao: Object.freeze({ max: 16, unit: "utf16_code_units" }),
|
|
12041
|
+
douyin: Object.freeze({ max: 30, unit: "utf16_code_units" }),
|
|
12042
|
+
bilibili: Object.freeze({ max: 80, unit: "utf16_code_units" })
|
|
12043
|
+
})
|
|
12044
|
+
}),
|
|
12045
|
+
bodyCapabilities: Object.freeze({
|
|
12046
|
+
dynamic: Object.freeze({
|
|
12047
|
+
threads: Object.freeze({ max: 500, unit: "unicode_code_points", behavior: "split_thread" }),
|
|
12048
|
+
x: Object.freeze({ max: 280, unit: "weighted", behavior: "split_thread" }),
|
|
12049
|
+
weibo: Object.freeze({ max: 2000, unit: "unicode_code_points" }),
|
|
12050
|
+
jike: Object.freeze({ max: 1000, unit: "unicode_code_points" })
|
|
12051
|
+
}),
|
|
12052
|
+
imageText: Object.freeze({
|
|
12053
|
+
xiaohongshu: Object.freeze({ max: 1000, unit: "unicode_code_points" }),
|
|
12054
|
+
douyin: Object.freeze({ max: 1000, unit: "unicode_code_points" })
|
|
12055
|
+
}),
|
|
12056
|
+
article: Object.freeze({
|
|
12057
|
+
wechatmp: Object.freeze({ max: 50000, unit: "unicode_code_points" }),
|
|
12058
|
+
juejin: Object.freeze({ max: 50000, unit: "unicode_code_points" })
|
|
12059
|
+
}),
|
|
12060
|
+
shortVideo: Object.freeze({
|
|
12061
|
+
douyin: Object.freeze({ max: 1000, unit: "unicode_code_points" }),
|
|
12062
|
+
kuaishou: Object.freeze({ max: 1000, unit: "unicode_code_points" }),
|
|
12063
|
+
shipinhao: Object.freeze({ max: 1000, unit: "unicode_code_points" })
|
|
12064
|
+
})
|
|
12065
|
+
})
|
|
12066
|
+
});
|
|
11990
12067
|
// ../../packages/kk-core/src/kinds.ts
|
|
11991
12068
|
init_zod();
|
|
11992
12069
|
|
|
@@ -12077,6 +12154,67 @@ var BrowserSessionDescriptorSchema = object2({
|
|
|
12077
12154
|
}
|
|
12078
12155
|
}).optional()
|
|
12079
12156
|
}).strict();
|
|
12157
|
+
var ContentCountUnitSchema = _enum([
|
|
12158
|
+
"utf16_code_units",
|
|
12159
|
+
"unicode_code_points",
|
|
12160
|
+
"weighted",
|
|
12161
|
+
"xiaohongshu_title_weighted"
|
|
12162
|
+
]);
|
|
12163
|
+
var LimitEntrySchema = object2({
|
|
12164
|
+
max: number2().int().positive(),
|
|
12165
|
+
unit: ContentCountUnitSchema
|
|
12166
|
+
}).strict();
|
|
12167
|
+
var PublishingProfileLimitsSchema = object2({
|
|
12168
|
+
title: LimitEntrySchema.optional(),
|
|
12169
|
+
body: LimitEntrySchema.optional()
|
|
12170
|
+
}).strict();
|
|
12171
|
+
var PublishingProfileRequiresSchema = object2({
|
|
12172
|
+
cover: boolean2().optional(),
|
|
12173
|
+
orderedImages: boolean2().optional()
|
|
12174
|
+
}).strict();
|
|
12175
|
+
var PublishingProfileModeBindingSchema = object2({
|
|
12176
|
+
metadata: record(string2(), unknown())
|
|
12177
|
+
}).strict();
|
|
12178
|
+
var PublishingProfileModesSchema = object2({
|
|
12179
|
+
draft: PublishingProfileModeBindingSchema.optional(),
|
|
12180
|
+
live: PublishingProfileModeBindingSchema.optional()
|
|
12181
|
+
}).strict().superRefine((modes, ctx) => {
|
|
12182
|
+
if (modes.draft === undefined && modes.live === undefined) {
|
|
12183
|
+
ctx.addIssue({
|
|
12184
|
+
code: "custom",
|
|
12185
|
+
message: 'modes must define at least one of "draft" or "live"'
|
|
12186
|
+
});
|
|
12187
|
+
}
|
|
12188
|
+
});
|
|
12189
|
+
var PublishingProfileSchema = object2({
|
|
12190
|
+
format: string2().min(1),
|
|
12191
|
+
locale: string2().regex(/^(source|[a-z]{2,3}(-[A-Za-z0-9]+)*)$/, {
|
|
12192
|
+
message: 'locale must be "source" or a valid language tag'
|
|
12193
|
+
}),
|
|
12194
|
+
limits: PublishingProfileLimitsSchema.optional(),
|
|
12195
|
+
requires: PublishingProfileRequiresSchema.optional(),
|
|
12196
|
+
modes: PublishingProfileModesSchema
|
|
12197
|
+
}).strict();
|
|
12198
|
+
var PublishingCapabilitiesSchema = object2({
|
|
12199
|
+
defaultProfile: string2().min(1),
|
|
12200
|
+
profiles: record(string2().regex(/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/), PublishingProfileSchema)
|
|
12201
|
+
}).strict().superRefine((data, ctx) => {
|
|
12202
|
+
const keys = Object.keys(data.profiles);
|
|
12203
|
+
if (keys.length === 0) {
|
|
12204
|
+
ctx.addIssue({
|
|
12205
|
+
code: "custom",
|
|
12206
|
+
path: ["profiles"],
|
|
12207
|
+
message: "profiles record must be nonempty"
|
|
12208
|
+
});
|
|
12209
|
+
}
|
|
12210
|
+
if (!(data.defaultProfile in data.profiles)) {
|
|
12211
|
+
ctx.addIssue({
|
|
12212
|
+
code: "custom",
|
|
12213
|
+
path: ["defaultProfile"],
|
|
12214
|
+
message: `defaultProfile "${data.defaultProfile}" must reference a declared profile`
|
|
12215
|
+
});
|
|
12216
|
+
}
|
|
12217
|
+
});
|
|
12080
12218
|
var PluginManifestSchemaBase = looseObject({
|
|
12081
12219
|
name: string2().min(1),
|
|
12082
12220
|
kind: PluginKindSchema,
|
|
@@ -12085,23 +12223,31 @@ var PluginManifestSchemaBase = looseObject({
|
|
|
12085
12223
|
version: string2().optional(),
|
|
12086
12224
|
description: string2().optional(),
|
|
12087
12225
|
operations: record(string2(), PluginOperationSchema).optional(),
|
|
12088
|
-
browserSession: BrowserSessionDescriptorSchema.optional()
|
|
12226
|
+
browserSession: BrowserSessionDescriptorSchema.optional(),
|
|
12227
|
+
publishing: PublishingCapabilitiesSchema.optional()
|
|
12089
12228
|
});
|
|
12090
12229
|
var PluginManifestSchema = PluginManifestSchemaBase.superRefine((manifest, ctx) => {
|
|
12091
|
-
if (manifest.browserSession
|
|
12092
|
-
|
|
12093
|
-
|
|
12094
|
-
|
|
12095
|
-
|
|
12096
|
-
|
|
12097
|
-
|
|
12098
|
-
}
|
|
12230
|
+
if (manifest.browserSession !== undefined) {
|
|
12231
|
+
if (manifest.kind !== "publish") {
|
|
12232
|
+
ctx.addIssue({
|
|
12233
|
+
code: "custom",
|
|
12234
|
+
path: ["browserSession"],
|
|
12235
|
+
message: `browserSession requires kind "publish", not "${manifest.kind}"`
|
|
12236
|
+
});
|
|
12237
|
+
}
|
|
12238
|
+
if (manifest.check === undefined) {
|
|
12239
|
+
ctx.addIssue({
|
|
12240
|
+
code: "custom",
|
|
12241
|
+
path: ["browserSession"],
|
|
12242
|
+
message: "browserSession requires a check probe entry that owns the session routines"
|
|
12243
|
+
});
|
|
12244
|
+
}
|
|
12099
12245
|
}
|
|
12100
|
-
if (manifest.
|
|
12246
|
+
if (manifest.publishing !== undefined && manifest.kind !== "publish") {
|
|
12101
12247
|
ctx.addIssue({
|
|
12102
12248
|
code: "custom",
|
|
12103
|
-
path: ["
|
|
12104
|
-
message:
|
|
12249
|
+
path: ["publishing"],
|
|
12250
|
+
message: `publishing requires kind "publish", not "${manifest.kind}"`
|
|
12105
12251
|
});
|
|
12106
12252
|
}
|
|
12107
12253
|
});
|
|
@@ -13549,6 +13695,9 @@ function createVoxCpmClient(options = {}) {
|
|
|
13549
13695
|
let renderInFlight;
|
|
13550
13696
|
let warnedChunk = false;
|
|
13551
13697
|
let warnedLanguage = false;
|
|
13698
|
+
let warnedInstruct = false;
|
|
13699
|
+
let warnedInstructIgnored = false;
|
|
13700
|
+
let instructSegments = 0;
|
|
13552
13701
|
const pending = new Map;
|
|
13553
13702
|
const renderResults = new Map;
|
|
13554
13703
|
const signalHandlers = new Map;
|
|
@@ -13747,6 +13896,14 @@ function createVoxCpmClient(options = {}) {
|
|
|
13747
13896
|
}
|
|
13748
13897
|
const requestedInstruct = body.instruct?.trim();
|
|
13749
13898
|
if (profile.mode === "instruct") {
|
|
13899
|
+
instructSegments += 1;
|
|
13900
|
+
if (instructSegments > 1) {
|
|
13901
|
+
throw new Error(`voxcpm-tts: profile "${profile.id}" is instruct-mode and pins no speaker \u2014 segment ${instructSegments} would sound like a different person; use a reference/continuation profile (VOXCPM_PROFILES_PATH)`);
|
|
13902
|
+
}
|
|
13903
|
+
if (!warnedInstruct) {
|
|
13904
|
+
warnedInstruct = true;
|
|
13905
|
+
echoError(`voxcpm-tts: profile "${profile.id}" is instruct-mode and pins no speaker \u2014 every request draws a new voice; use a reference/continuation profile for multi-segment episodes`);
|
|
13906
|
+
}
|
|
13750
13907
|
const instruct = requestedInstruct || profile.instruct;
|
|
13751
13908
|
if (instruct) {
|
|
13752
13909
|
return { instruct, normalize: body.normalize ?? true };
|
|
@@ -13754,7 +13911,10 @@ function createVoxCpmClient(options = {}) {
|
|
|
13754
13911
|
return { normalize: body.normalize ?? true };
|
|
13755
13912
|
}
|
|
13756
13913
|
if (requestedInstruct) {
|
|
13757
|
-
|
|
13914
|
+
if (!warnedInstructIgnored) {
|
|
13915
|
+
warnedInstructIgnored = true;
|
|
13916
|
+
echoError(`voxcpm-tts: ignoring the per-request instruct on "${profile.mode}" profile "${profile.id}" \u2014 the reference clip defines the voice`);
|
|
13917
|
+
}
|
|
13758
13918
|
}
|
|
13759
13919
|
return { normalize: body.normalize ?? true };
|
|
13760
13920
|
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Grade one rendered episode for speaker consistency (音色/音质 uniformity).
|
|
3
|
+
|
|
4
|
+
Why this exists: a profile whose ``mode`` is ``instruct`` carries no reference audio, so VoxCPM
|
|
5
|
+
samples a speaker per request and a multi-segment episode changes voice between segments. The
|
|
6
|
+
2026-10-15 episode drew 59 different speakers (within-item F0 swing up to 103 Hz) and nobody
|
|
7
|
+
noticed until a human listened. This script makes that measurable.
|
|
8
|
+
|
|
9
|
+
Usage (needs the plugin venv, which has librosa/numpy):
|
|
10
|
+
|
|
11
|
+
plugins/generations/voxcpm-tts/.venv/bin/python \
|
|
12
|
+
plugins/generations/voxcpm-tts/scripts/audit-speaker-consistency.py <content.json> [more.json ...]
|
|
13
|
+
|
|
14
|
+
Pass an episode content JSON (grouped per item when the sibling ``sections/<date>/`` dir exists)
|
|
15
|
+
or individual section JSONs. Exit code 1 means the thresholds below were missed.
|
|
16
|
+
|
|
17
|
+
Thresholds come from the two measured modes (2026-10-15, same three texts):
|
|
18
|
+
reference/continuation anchored -> F0 spread 22-26 Hz (one speaker)
|
|
19
|
+
instruct (random speaker) -> F0 spread 141 Hz (three texts, three speakers)
|
|
20
|
+
Grade "pass" needs every item's F0 spread <= 45 Hz and the per-item median <= 30 Hz. Median pitch
|
|
21
|
+
is the honest signal here: it tracks the speaker register, not the sentence's intonation.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import json
|
|
27
|
+
import pathlib
|
|
28
|
+
import sys
|
|
29
|
+
import warnings
|
|
30
|
+
|
|
31
|
+
import librosa
|
|
32
|
+
import numpy as np
|
|
33
|
+
|
|
34
|
+
warnings.filterwarnings('ignore')
|
|
35
|
+
|
|
36
|
+
SR = 16000
|
|
37
|
+
MAX_ITEM_F0_SPREAD = 45.0
|
|
38
|
+
MAX_ITEM_MEDIAN_SPREAD = 30.0
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def measure(content_path: pathlib.Path) -> list[float]:
|
|
42
|
+
"""Per-segment median F0 (Hz) of one rendered Content."""
|
|
43
|
+
content = json.loads(content_path.read_text())
|
|
44
|
+
wav_path = content_path.parent / f'{content_path.stem}.wav'
|
|
45
|
+
audio, _ = librosa.load(str(wav_path), sr=SR, mono=True)
|
|
46
|
+
medians, offset = [], 0.0
|
|
47
|
+
for segment in content['metadata']['segments']:
|
|
48
|
+
start = int(offset * SR)
|
|
49
|
+
offset += segment['duration']
|
|
50
|
+
window = audio[start : int(offset * SR)]
|
|
51
|
+
if len(window) < SR * 0.4:
|
|
52
|
+
continue
|
|
53
|
+
f0 = librosa.yin(window, fmin=70, fmax=350, sr=SR)
|
|
54
|
+
voiced = f0[(f0 > 80) & (f0 < 340)]
|
|
55
|
+
if len(voiced):
|
|
56
|
+
medians.append(float(np.median(voiced)))
|
|
57
|
+
return medians
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def grouping(content_path: pathlib.Path) -> list[tuple[str, pathlib.Path]]:
|
|
61
|
+
"""Items to grade: the section JSONs when they sit next to the episode, else the file itself."""
|
|
62
|
+
sections = content_path.parent / 'sections' / content_path.stem.split('_')[0]
|
|
63
|
+
if content_path.name.endswith('_11_generate_content.json') and sections.is_dir():
|
|
64
|
+
return sorted((p.stem, p) for p in sections.glob('*.json'))
|
|
65
|
+
return [(content_path.stem, content_path)]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def main(argv: list[str]) -> int:
|
|
69
|
+
if not argv:
|
|
70
|
+
print(__doc__.strip().splitlines()[0], file=sys.stderr)
|
|
71
|
+
print('usage: audit-speaker-consistency.py <content.json> [...]', file=sys.stderr)
|
|
72
|
+
return 2
|
|
73
|
+
|
|
74
|
+
failures: list[str] = []
|
|
75
|
+
spreads: list[float] = []
|
|
76
|
+
print(f'{"item":14s} {"segs":>4s} {"medF0":>6s} {"spread":>7s} verdict')
|
|
77
|
+
for target in argv:
|
|
78
|
+
path = pathlib.Path(target)
|
|
79
|
+
for name, section_path in grouping(path):
|
|
80
|
+
medians = measure(section_path)
|
|
81
|
+
if len(medians) < 2:
|
|
82
|
+
continue
|
|
83
|
+
spread = max(medians) - min(medians)
|
|
84
|
+
spreads.append(spread)
|
|
85
|
+
verdict = 'ok'
|
|
86
|
+
if spread > MAX_ITEM_F0_SPREAD:
|
|
87
|
+
verdict = f'unstable (> {MAX_ITEM_F0_SPREAD:.0f} Hz)'
|
|
88
|
+
failures.append(f'{name}: F0 spread {spread:.0f} Hz across {len(medians)} segments')
|
|
89
|
+
print(f'{name:14s} {len(medians):4d} {np.median(medians):6.0f} {spread:7.0f} {verdict}')
|
|
90
|
+
|
|
91
|
+
if not spreads:
|
|
92
|
+
print('no item had enough segments to grade', file=sys.stderr)
|
|
93
|
+
return 2
|
|
94
|
+
|
|
95
|
+
median_spread = float(np.median(spreads))
|
|
96
|
+
print(f'\nper-item F0 spread: median {median_spread:.0f} Hz, worst {max(spreads):.0f} Hz over {len(spreads)} items')
|
|
97
|
+
if median_spread > MAX_ITEM_MEDIAN_SPREAD:
|
|
98
|
+
failures.append(f'median per-item F0 spread {median_spread:.0f} Hz > {MAX_ITEM_MEDIAN_SPREAD:.0f} Hz')
|
|
99
|
+
if failures:
|
|
100
|
+
print('FAIL — the episode changes speaker between segments (check the profile mode):')
|
|
101
|
+
for line in failures:
|
|
102
|
+
print(f' - {line}')
|
|
103
|
+
return 1
|
|
104
|
+
print('PASS — one speaker across every item')
|
|
105
|
+
return 0
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
if __name__ == '__main__':
|
|
109
|
+
sys.exit(main(sys.argv[1:]))
|
|
@@ -273,6 +273,9 @@ export function createVoxCpmClient(options: VoxCpmClientOptions = {}): VoxCpmCli
|
|
|
273
273
|
let renderInFlight: { id: number; dest: string } | undefined;
|
|
274
274
|
let warnedChunk = false;
|
|
275
275
|
let warnedLanguage = false;
|
|
276
|
+
let warnedInstruct = false;
|
|
277
|
+
let warnedInstructIgnored = false;
|
|
278
|
+
let instructSegments = 0;
|
|
276
279
|
const pending = new Map<number, PendingRequest>();
|
|
277
280
|
const renderResults = new Map<number, Promise<Record<string, unknown>>>();
|
|
278
281
|
const signalHandlers = new Map<NodeJS.Signals, () => void>();
|
|
@@ -509,6 +512,22 @@ export function createVoxCpmClient(options: VoxCpmClientOptions = {}): VoxCpmCli
|
|
|
509
512
|
}
|
|
510
513
|
const requestedInstruct = body.instruct?.trim();
|
|
511
514
|
if (profile.mode === 'instruct') {
|
|
515
|
+
// Instruct profiles carry no reference audio, so the model samples a speaker per request:
|
|
516
|
+
// a multi-segment episode then changes voice from segment to segment (20261015: within-item
|
|
517
|
+
// F0 swing up to 103 Hz, 59 speakers). One segment is still legitimate; a second one is the
|
|
518
|
+
// defect, so refuse it instead of shipping a voice change.
|
|
519
|
+
instructSegments += 1;
|
|
520
|
+
if (instructSegments > 1) {
|
|
521
|
+
throw new Error(
|
|
522
|
+
`voxcpm-tts: profile "${profile.id}" is instruct-mode and pins no speaker — segment ${instructSegments} would sound like a different person; use a reference/continuation profile (VOXCPM_PROFILES_PATH)`,
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
if (!warnedInstruct) {
|
|
526
|
+
warnedInstruct = true;
|
|
527
|
+
echoError(
|
|
528
|
+
`voxcpm-tts: profile "${profile.id}" is instruct-mode and pins no speaker — every request draws a new voice; use a reference/continuation profile for multi-segment episodes`,
|
|
529
|
+
);
|
|
530
|
+
}
|
|
512
531
|
const instruct = requestedInstruct || profile.instruct;
|
|
513
532
|
if (instruct) {
|
|
514
533
|
return { instruct, normalize: body.normalize ?? true };
|
|
@@ -516,7 +535,15 @@ export function createVoxCpmClient(options: VoxCpmClientOptions = {}): VoxCpmCli
|
|
|
516
535
|
return { normalize: body.normalize ?? true };
|
|
517
536
|
}
|
|
518
537
|
if (requestedInstruct) {
|
|
519
|
-
|
|
538
|
+
// The shared pipeline turns a segment's `emotion` into a per-request instruct, so this is
|
|
539
|
+
// the normal case for every daily episode. A reference/continuation profile already fixes
|
|
540
|
+
// the voice: ignoring the instruction beats failing the tier (or re-labelling the clip).
|
|
541
|
+
if (!warnedInstructIgnored) {
|
|
542
|
+
warnedInstructIgnored = true;
|
|
543
|
+
echoError(
|
|
544
|
+
`voxcpm-tts: ignoring the per-request instruct on "${profile.mode}" profile "${profile.id}" — the reference clip defines the voice`,
|
|
545
|
+
);
|
|
546
|
+
}
|
|
520
547
|
}
|
|
521
548
|
return { normalize: body.normalize ?? true };
|
|
522
549
|
}
|
|
@@ -12236,6 +12236,83 @@ var init_file_system_node = () => {};
|
|
|
12236
12236
|
import { dirname as dirname2 } from "path";
|
|
12237
12237
|
import { parseArgs } from "util";
|
|
12238
12238
|
|
|
12239
|
+
// ../../packages/kk-core/src/content-limits.ts
|
|
12240
|
+
var PLATFORM_CONTENT_LIMITS = Object.freeze({
|
|
12241
|
+
schemaVersion: 5,
|
|
12242
|
+
tagLimits: Object.freeze({
|
|
12243
|
+
default: 7,
|
|
12244
|
+
imageText: Object.freeze({
|
|
12245
|
+
instagram: 5,
|
|
12246
|
+
douyin: 5,
|
|
12247
|
+
kuaishou: 4,
|
|
12248
|
+
weixin: 1,
|
|
12249
|
+
xiaohongshu: 10
|
|
12250
|
+
}),
|
|
12251
|
+
shortVideo: Object.freeze({
|
|
12252
|
+
douyin: 5,
|
|
12253
|
+
kuaishou: 4,
|
|
12254
|
+
xiaohongshu: 10,
|
|
12255
|
+
bilibili: 10,
|
|
12256
|
+
zhihu: 5,
|
|
12257
|
+
tiktok: 20,
|
|
12258
|
+
youtube: 60
|
|
12259
|
+
})
|
|
12260
|
+
}),
|
|
12261
|
+
titleCapabilities: Object.freeze({
|
|
12262
|
+
dynamic: Object.freeze({
|
|
12263
|
+
bilibili: Object.freeze({ max: 20, unit: "utf16_code_units" }),
|
|
12264
|
+
zhihu: Object.freeze({ max: 50, unit: "utf16_code_units" }),
|
|
12265
|
+
douban: Object.freeze({ max: 70, unit: "utf16_code_units" }),
|
|
12266
|
+
x: null,
|
|
12267
|
+
threads: null,
|
|
12268
|
+
weibo: null,
|
|
12269
|
+
jike: null,
|
|
12270
|
+
zsxq: null,
|
|
12271
|
+
toutiao: null,
|
|
12272
|
+
juejin: null
|
|
12273
|
+
}),
|
|
12274
|
+
imageText: Object.freeze({
|
|
12275
|
+
xiaohongshu: Object.freeze({ max: 20, unit: "xiaohongshu_title_weighted" }),
|
|
12276
|
+
weixin: Object.freeze({ max: 20, unit: "utf16_code_units" }),
|
|
12277
|
+
douyin: Object.freeze({ max: 20, unit: "utf16_code_units" }),
|
|
12278
|
+
shipinhao: Object.freeze({ max: 22, unit: "utf16_code_units" }),
|
|
12279
|
+
reddit: Object.freeze({ max: 300, unit: "utf16_code_units" })
|
|
12280
|
+
}),
|
|
12281
|
+
article: Object.freeze({
|
|
12282
|
+
juejin: Object.freeze({ max: 100, unit: "utf16_code_units" }),
|
|
12283
|
+
csdn: Object.freeze({ max: 100, unit: "utf16_code_units" }),
|
|
12284
|
+
cnblogs: Object.freeze({ max: 128, unit: "utf16_code_units" }),
|
|
12285
|
+
weibo: Object.freeze({ max: 30, unit: "utf16_code_units" }),
|
|
12286
|
+
sspai: Object.freeze({ max: 80, unit: "utf16_code_units" })
|
|
12287
|
+
}),
|
|
12288
|
+
shortVideo: Object.freeze({
|
|
12289
|
+
shipinhao: Object.freeze({ max: 16, unit: "utf16_code_units" }),
|
|
12290
|
+
douyin: Object.freeze({ max: 30, unit: "utf16_code_units" }),
|
|
12291
|
+
bilibili: Object.freeze({ max: 80, unit: "utf16_code_units" })
|
|
12292
|
+
})
|
|
12293
|
+
}),
|
|
12294
|
+
bodyCapabilities: Object.freeze({
|
|
12295
|
+
dynamic: Object.freeze({
|
|
12296
|
+
threads: Object.freeze({ max: 500, unit: "unicode_code_points", behavior: "split_thread" }),
|
|
12297
|
+
x: Object.freeze({ max: 280, unit: "weighted", behavior: "split_thread" }),
|
|
12298
|
+
weibo: Object.freeze({ max: 2000, unit: "unicode_code_points" }),
|
|
12299
|
+
jike: Object.freeze({ max: 1000, unit: "unicode_code_points" })
|
|
12300
|
+
}),
|
|
12301
|
+
imageText: Object.freeze({
|
|
12302
|
+
xiaohongshu: Object.freeze({ max: 1000, unit: "unicode_code_points" }),
|
|
12303
|
+
douyin: Object.freeze({ max: 1000, unit: "unicode_code_points" })
|
|
12304
|
+
}),
|
|
12305
|
+
article: Object.freeze({
|
|
12306
|
+
wechatmp: Object.freeze({ max: 50000, unit: "unicode_code_points" }),
|
|
12307
|
+
juejin: Object.freeze({ max: 50000, unit: "unicode_code_points" })
|
|
12308
|
+
}),
|
|
12309
|
+
shortVideo: Object.freeze({
|
|
12310
|
+
douyin: Object.freeze({ max: 1000, unit: "unicode_code_points" }),
|
|
12311
|
+
kuaishou: Object.freeze({ max: 1000, unit: "unicode_code_points" }),
|
|
12312
|
+
shipinhao: Object.freeze({ max: 1000, unit: "unicode_code_points" })
|
|
12313
|
+
})
|
|
12314
|
+
})
|
|
12315
|
+
});
|
|
12239
12316
|
// ../../packages/kk-core/src/kinds.ts
|
|
12240
12317
|
init_zod();
|
|
12241
12318
|
|
|
@@ -12326,6 +12403,67 @@ var BrowserSessionDescriptorSchema = object({
|
|
|
12326
12403
|
}
|
|
12327
12404
|
}).optional()
|
|
12328
12405
|
}).strict();
|
|
12406
|
+
var ContentCountUnitSchema = _enum([
|
|
12407
|
+
"utf16_code_units",
|
|
12408
|
+
"unicode_code_points",
|
|
12409
|
+
"weighted",
|
|
12410
|
+
"xiaohongshu_title_weighted"
|
|
12411
|
+
]);
|
|
12412
|
+
var LimitEntrySchema = object({
|
|
12413
|
+
max: number2().int().positive(),
|
|
12414
|
+
unit: ContentCountUnitSchema
|
|
12415
|
+
}).strict();
|
|
12416
|
+
var PublishingProfileLimitsSchema = object({
|
|
12417
|
+
title: LimitEntrySchema.optional(),
|
|
12418
|
+
body: LimitEntrySchema.optional()
|
|
12419
|
+
}).strict();
|
|
12420
|
+
var PublishingProfileRequiresSchema = object({
|
|
12421
|
+
cover: boolean2().optional(),
|
|
12422
|
+
orderedImages: boolean2().optional()
|
|
12423
|
+
}).strict();
|
|
12424
|
+
var PublishingProfileModeBindingSchema = object({
|
|
12425
|
+
metadata: record(string2(), unknown())
|
|
12426
|
+
}).strict();
|
|
12427
|
+
var PublishingProfileModesSchema = object({
|
|
12428
|
+
draft: PublishingProfileModeBindingSchema.optional(),
|
|
12429
|
+
live: PublishingProfileModeBindingSchema.optional()
|
|
12430
|
+
}).strict().superRefine((modes, ctx) => {
|
|
12431
|
+
if (modes.draft === undefined && modes.live === undefined) {
|
|
12432
|
+
ctx.addIssue({
|
|
12433
|
+
code: "custom",
|
|
12434
|
+
message: 'modes must define at least one of "draft" or "live"'
|
|
12435
|
+
});
|
|
12436
|
+
}
|
|
12437
|
+
});
|
|
12438
|
+
var PublishingProfileSchema = object({
|
|
12439
|
+
format: string2().min(1),
|
|
12440
|
+
locale: string2().regex(/^(source|[a-z]{2,3}(-[A-Za-z0-9]+)*)$/, {
|
|
12441
|
+
message: 'locale must be "source" or a valid language tag'
|
|
12442
|
+
}),
|
|
12443
|
+
limits: PublishingProfileLimitsSchema.optional(),
|
|
12444
|
+
requires: PublishingProfileRequiresSchema.optional(),
|
|
12445
|
+
modes: PublishingProfileModesSchema
|
|
12446
|
+
}).strict();
|
|
12447
|
+
var PublishingCapabilitiesSchema = object({
|
|
12448
|
+
defaultProfile: string2().min(1),
|
|
12449
|
+
profiles: record(string2().regex(/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/), PublishingProfileSchema)
|
|
12450
|
+
}).strict().superRefine((data, ctx) => {
|
|
12451
|
+
const keys = Object.keys(data.profiles);
|
|
12452
|
+
if (keys.length === 0) {
|
|
12453
|
+
ctx.addIssue({
|
|
12454
|
+
code: "custom",
|
|
12455
|
+
path: ["profiles"],
|
|
12456
|
+
message: "profiles record must be nonempty"
|
|
12457
|
+
});
|
|
12458
|
+
}
|
|
12459
|
+
if (!(data.defaultProfile in data.profiles)) {
|
|
12460
|
+
ctx.addIssue({
|
|
12461
|
+
code: "custom",
|
|
12462
|
+
path: ["defaultProfile"],
|
|
12463
|
+
message: `defaultProfile "${data.defaultProfile}" must reference a declared profile`
|
|
12464
|
+
});
|
|
12465
|
+
}
|
|
12466
|
+
});
|
|
12329
12467
|
var PluginManifestSchemaBase = looseObject({
|
|
12330
12468
|
name: string2().min(1),
|
|
12331
12469
|
kind: PluginKindSchema,
|
|
@@ -12334,23 +12472,31 @@ var PluginManifestSchemaBase = looseObject({
|
|
|
12334
12472
|
version: string2().optional(),
|
|
12335
12473
|
description: string2().optional(),
|
|
12336
12474
|
operations: record(string2(), PluginOperationSchema).optional(),
|
|
12337
|
-
browserSession: BrowserSessionDescriptorSchema.optional()
|
|
12475
|
+
browserSession: BrowserSessionDescriptorSchema.optional(),
|
|
12476
|
+
publishing: PublishingCapabilitiesSchema.optional()
|
|
12338
12477
|
});
|
|
12339
12478
|
var PluginManifestSchema = PluginManifestSchemaBase.superRefine((manifest, ctx) => {
|
|
12340
|
-
if (manifest.browserSession
|
|
12341
|
-
|
|
12342
|
-
|
|
12343
|
-
|
|
12344
|
-
|
|
12345
|
-
|
|
12346
|
-
|
|
12347
|
-
}
|
|
12479
|
+
if (manifest.browserSession !== undefined) {
|
|
12480
|
+
if (manifest.kind !== "publish") {
|
|
12481
|
+
ctx.addIssue({
|
|
12482
|
+
code: "custom",
|
|
12483
|
+
path: ["browserSession"],
|
|
12484
|
+
message: `browserSession requires kind "publish", not "${manifest.kind}"`
|
|
12485
|
+
});
|
|
12486
|
+
}
|
|
12487
|
+
if (manifest.check === undefined) {
|
|
12488
|
+
ctx.addIssue({
|
|
12489
|
+
code: "custom",
|
|
12490
|
+
path: ["browserSession"],
|
|
12491
|
+
message: "browserSession requires a check probe entry that owns the session routines"
|
|
12492
|
+
});
|
|
12493
|
+
}
|
|
12348
12494
|
}
|
|
12349
|
-
if (manifest.
|
|
12495
|
+
if (manifest.publishing !== undefined && manifest.kind !== "publish") {
|
|
12350
12496
|
ctx.addIssue({
|
|
12351
12497
|
code: "custom",
|
|
12352
|
-
path: ["
|
|
12353
|
-
message:
|
|
12498
|
+
path: ["publishing"],
|
|
12499
|
+
message: `publishing requires kind "publish", not "${manifest.kind}"`
|
|
12354
12500
|
});
|
|
12355
12501
|
}
|
|
12356
12502
|
});
|