@roll-agent/smart-reply-agent 0.1.0 → 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.
Files changed (129) hide show
  1. package/dist/ai/model-registry.d.ts +0 -1
  2. package/dist/ai/model-registry.js +1 -205
  3. package/dist/ai/structured-output.d.ts +0 -1
  4. package/dist/ai/structured-output.js +1 -78
  5. package/dist/errors/app-error.d.ts +0 -1
  6. package/dist/errors/app-error.js +1 -95
  7. package/dist/errors/error-codes.d.ts +0 -1
  8. package/dist/errors/error-codes.js +1 -115
  9. package/dist/errors/error-factory.d.ts +0 -1
  10. package/dist/errors/error-factory.js +1 -86
  11. package/dist/errors/error-utils.d.ts +0 -1
  12. package/dist/errors/error-utils.js +1 -188
  13. package/dist/errors/index.d.ts +0 -1
  14. package/dist/errors/index.js +1 -5
  15. package/dist/index.d.ts +0 -1
  16. package/dist/index.js +1 -12
  17. package/dist/log-control.d.ts +0 -1
  18. package/dist/log-control.js +1 -15
  19. package/dist/pipeline/age-eligibility.d.ts +0 -1
  20. package/dist/pipeline/age-eligibility.js +1 -176
  21. package/dist/pipeline/candidate-context.d.ts +0 -1
  22. package/dist/pipeline/candidate-context.js +1 -31
  23. package/dist/pipeline/candidate-utils.d.ts +0 -1
  24. package/dist/pipeline/candidate-utils.js +1 -33
  25. package/dist/pipeline/classification.d.ts +0 -1
  26. package/dist/pipeline/classification.js +1 -206
  27. package/dist/pipeline/context-builder.d.ts +0 -1
  28. package/dist/pipeline/context-builder.js +1 -404
  29. package/dist/pipeline/pipeline-progress.d.ts +0 -1
  30. package/dist/pipeline/pipeline-progress.js +1 -33
  31. package/dist/pipeline/reply-gate.d.ts +0 -1
  32. package/dist/pipeline/reply-gate.js +1 -139
  33. package/dist/pipeline/smart-reply.d.ts +0 -1
  34. package/dist/pipeline/smart-reply.js +1 -418
  35. package/dist/pipeline.d.ts +0 -1
  36. package/dist/pipeline.js +1 -12
  37. package/dist/services/brand-alias.d.ts +0 -1
  38. package/dist/services/brand-alias.js +1 -184
  39. package/dist/services/brand-config-selectors.d.ts +0 -1
  40. package/dist/services/brand-config-selectors.js +1 -30
  41. package/dist/services/config-loader.d.ts +0 -1
  42. package/dist/services/config-loader.js +1 -45
  43. package/dist/services/duliday-api.d.ts +0 -1
  44. package/dist/services/duliday-api.js +1 -160
  45. package/dist/services/duliday-mapper.d.ts +0 -1
  46. package/dist/services/duliday-mapper.js +1 -536
  47. package/dist/tools/generate-reply.d.ts +0 -1
  48. package/dist/tools/generate-reply.js +1 -132
  49. package/dist/tools/sync-brand-data.d.ts +0 -1
  50. package/dist/tools/sync-brand-data.js +1 -114
  51. package/dist/types/brand-resolution.d.ts +0 -1
  52. package/dist/types/brand-resolution.js +1 -37
  53. package/dist/types/classification.d.ts +0 -1
  54. package/dist/types/classification.js +1 -30
  55. package/dist/types/config.d.ts +0 -1
  56. package/dist/types/config.js +1 -7
  57. package/dist/types/duliday-api.d.ts +0 -1
  58. package/dist/types/duliday-api.js +1 -235
  59. package/dist/types/geocoding.d.ts +0 -1
  60. package/dist/types/geocoding.js +1 -12
  61. package/dist/types/reply-policy.d.ts +0 -1
  62. package/dist/types/reply-policy.js +1 -332
  63. package/dist/types/zhipin.d.ts +0 -1
  64. package/dist/types/zhipin.js +1 -123
  65. package/package.json +3 -3
  66. package/dist/ai/model-registry.d.ts.map +0 -1
  67. package/dist/ai/model-registry.js.map +0 -1
  68. package/dist/ai/structured-output.d.ts.map +0 -1
  69. package/dist/ai/structured-output.js.map +0 -1
  70. package/dist/errors/app-error.d.ts.map +0 -1
  71. package/dist/errors/app-error.js.map +0 -1
  72. package/dist/errors/error-codes.d.ts.map +0 -1
  73. package/dist/errors/error-codes.js.map +0 -1
  74. package/dist/errors/error-factory.d.ts.map +0 -1
  75. package/dist/errors/error-factory.js.map +0 -1
  76. package/dist/errors/error-utils.d.ts.map +0 -1
  77. package/dist/errors/error-utils.js.map +0 -1
  78. package/dist/errors/index.d.ts.map +0 -1
  79. package/dist/errors/index.js.map +0 -1
  80. package/dist/index.d.ts.map +0 -1
  81. package/dist/index.js.map +0 -1
  82. package/dist/log-control.d.ts.map +0 -1
  83. package/dist/log-control.js.map +0 -1
  84. package/dist/pipeline/age-eligibility.d.ts.map +0 -1
  85. package/dist/pipeline/age-eligibility.js.map +0 -1
  86. package/dist/pipeline/candidate-context.d.ts.map +0 -1
  87. package/dist/pipeline/candidate-context.js.map +0 -1
  88. package/dist/pipeline/candidate-utils.d.ts.map +0 -1
  89. package/dist/pipeline/candidate-utils.js.map +0 -1
  90. package/dist/pipeline/classification.d.ts.map +0 -1
  91. package/dist/pipeline/classification.js.map +0 -1
  92. package/dist/pipeline/context-builder.d.ts.map +0 -1
  93. package/dist/pipeline/context-builder.js.map +0 -1
  94. package/dist/pipeline/pipeline-progress.d.ts.map +0 -1
  95. package/dist/pipeline/pipeline-progress.js.map +0 -1
  96. package/dist/pipeline/reply-gate.d.ts.map +0 -1
  97. package/dist/pipeline/reply-gate.js.map +0 -1
  98. package/dist/pipeline/smart-reply.d.ts.map +0 -1
  99. package/dist/pipeline/smart-reply.js.map +0 -1
  100. package/dist/pipeline.d.ts.map +0 -1
  101. package/dist/pipeline.js.map +0 -1
  102. package/dist/services/brand-alias.d.ts.map +0 -1
  103. package/dist/services/brand-alias.js.map +0 -1
  104. package/dist/services/brand-config-selectors.d.ts.map +0 -1
  105. package/dist/services/brand-config-selectors.js.map +0 -1
  106. package/dist/services/config-loader.d.ts.map +0 -1
  107. package/dist/services/config-loader.js.map +0 -1
  108. package/dist/services/duliday-api.d.ts.map +0 -1
  109. package/dist/services/duliday-api.js.map +0 -1
  110. package/dist/services/duliday-mapper.d.ts.map +0 -1
  111. package/dist/services/duliday-mapper.js.map +0 -1
  112. package/dist/tools/generate-reply.d.ts.map +0 -1
  113. package/dist/tools/generate-reply.js.map +0 -1
  114. package/dist/tools/sync-brand-data.d.ts.map +0 -1
  115. package/dist/tools/sync-brand-data.js.map +0 -1
  116. package/dist/types/brand-resolution.d.ts.map +0 -1
  117. package/dist/types/brand-resolution.js.map +0 -1
  118. package/dist/types/classification.d.ts.map +0 -1
  119. package/dist/types/classification.js.map +0 -1
  120. package/dist/types/config.d.ts.map +0 -1
  121. package/dist/types/config.js.map +0 -1
  122. package/dist/types/duliday-api.d.ts.map +0 -1
  123. package/dist/types/duliday-api.js.map +0 -1
  124. package/dist/types/geocoding.d.ts.map +0 -1
  125. package/dist/types/geocoding.js.map +0 -1
  126. package/dist/types/reply-policy.d.ts.map +0 -1
  127. package/dist/types/reply-policy.js.map +0 -1
  128. package/dist/types/zhipin.d.ts.map +0 -1
  129. package/dist/types/zhipin.js.map +0 -1
@@ -1,188 +1 @@
1
- import { NoObjectGeneratedError } from "ai";
2
- import { AppError, isAppError } from "./app-error.js";
3
- import { ErrorCode as EC } from "./error-codes.js";
4
- import { createLLMError, createNetworkError, createStructuredOutputError, } from "./error-factory.js";
5
- export function parseAISDKError(error) {
6
- if (!error || typeof error !== "object")
7
- return null;
8
- const err = error;
9
- const isAISDKError = err.name === "AI_APICallError" ||
10
- err.name === "APICallError" ||
11
- (typeof err.url === "string" && typeof err.statusCode === "number");
12
- if (!isAISDKError)
13
- return null;
14
- const statusCode = typeof err.statusCode === "number" ? err.statusCode : undefined;
15
- const responseBody = typeof err.responseBody === "string" ? err.responseBody : undefined;
16
- const url = typeof err.url === "string" ? err.url : undefined;
17
- const message = err.message;
18
- let provider;
19
- if (url) {
20
- if (url.includes("openai.com"))
21
- provider = "openai";
22
- else if (url.includes("anthropic.com"))
23
- provider = "anthropic";
24
- else if (url.includes("dashscope.aliyuncs.com"))
25
- provider = "qwen";
26
- else if (url.includes("openrouter.ai"))
27
- provider = "openrouter";
28
- else if (url.includes("deepseek.com"))
29
- provider = "deepseek";
30
- else if (url.includes("moonshot.cn"))
31
- provider = "moonshotai";
32
- else if (url.includes("googleapis.com"))
33
- provider = "google";
34
- }
35
- let model;
36
- if (responseBody) {
37
- const modelMatch = responseBody.match(/model[`'":\s]+([^`'"}\s,]+)/i);
38
- if (modelMatch)
39
- model = modelMatch[1];
40
- }
41
- return {
42
- isAuthError: statusCode === 401 || statusCode === 403,
43
- isModelNotFound: (responseBody?.includes("model") && responseBody?.includes("not exist")) ||
44
- responseBody?.includes("not authorized to access this model") ||
45
- false,
46
- isRateLimited: statusCode === 429,
47
- isTimeout: statusCode === 408 ||
48
- statusCode === 504 ||
49
- message?.toLowerCase().includes("timeout") ||
50
- false,
51
- statusCode,
52
- provider,
53
- model,
54
- originalMessage: message,
55
- responseBody,
56
- };
57
- }
58
- function detectMarkdownFormat(text) {
59
- const markdownPatterns = [
60
- /^```/m,
61
- /^#{1,6}\s/m,
62
- /^\s*[-*+]\s/m,
63
- /^\s*\d+\.\s/m,
64
- /\[.+\]\(.+\)/,
65
- /^\s*>/m,
66
- ];
67
- return markdownPatterns.some((pattern) => pattern.test(text));
68
- }
69
- export function parseNoObjectGeneratedError(error) {
70
- try {
71
- if (typeof NoObjectGeneratedError === "undefined" ||
72
- typeof NoObjectGeneratedError.isInstance !== "function") {
73
- return null;
74
- }
75
- if (!NoObjectGeneratedError.isInstance(error))
76
- return null;
77
- }
78
- catch {
79
- return null;
80
- }
81
- const rawText = error.text;
82
- const isMarkdownFormat = rawText ? detectMarkdownFormat(rawText) : false;
83
- const response = error.response
84
- ? {
85
- id: error.response.id,
86
- timestamp: error.response.timestamp,
87
- modelId: error.response.modelId,
88
- }
89
- : undefined;
90
- const usage = error.usage ?? undefined;
91
- const result = { isNoObjectGeneratedError: true, isMarkdownFormat };
92
- if (rawText !== undefined)
93
- result.rawText = rawText;
94
- if (error.cause instanceof Error)
95
- result.cause = error.cause;
96
- if (response !== undefined)
97
- result.response = response;
98
- if (usage !== undefined)
99
- result.usage = usage;
100
- return result;
101
- }
102
- export function wrapError(error, fallbackCode = EC.SYSTEM_UNKNOWN, userMessage) {
103
- if (isAppError(error)) {
104
- if (userMessage && userMessage !== error.userMessage) {
105
- return new AppError({
106
- code: error.code,
107
- message: error.message,
108
- userMessage,
109
- cause: error.cause,
110
- details: error.details,
111
- });
112
- }
113
- return error;
114
- }
115
- const originalError = toError(error);
116
- const aiInfo = parseAISDKError(error);
117
- if (aiInfo) {
118
- const context = {
119
- model: aiInfo.model,
120
- provider: aiInfo.provider,
121
- statusCode: aiInfo.statusCode,
122
- responseBody: aiInfo.responseBody,
123
- };
124
- if (aiInfo.isModelNotFound) {
125
- return createLLMError(EC.LLM_MODEL_NOT_FOUND, originalError, context);
126
- }
127
- if (aiInfo.isAuthError)
128
- return createLLMError(EC.LLM_UNAUTHORIZED, originalError, context);
129
- if (aiInfo.isRateLimited)
130
- return createLLMError(EC.LLM_RATE_LIMITED, originalError, context);
131
- if (aiInfo.isTimeout)
132
- return createLLMError(EC.LLM_TIMEOUT, originalError, context);
133
- return createLLMError(EC.LLM_GENERATION_FAILED, originalError, context);
134
- }
135
- const noObjectInfo = parseNoObjectGeneratedError(error);
136
- if (noObjectInfo) {
137
- return createStructuredOutputError(EC.LLM_RESPONSE_PARSE_ERROR, originalError, {
138
- rawText: noObjectInfo.rawText,
139
- isMarkdownFormat: noObjectInfo.isMarkdownFormat,
140
- parseErrorMessage: noObjectInfo.cause?.message,
141
- usage: noObjectInfo.usage,
142
- });
143
- }
144
- const message = originalError.message.toLowerCase();
145
- if (message.includes("timeout") ||
146
- message.includes("econnrefused") ||
147
- message.includes("network") ||
148
- message.includes("fetch failed")) {
149
- if (message.includes("timeout"))
150
- return createNetworkError(EC.NETWORK_TIMEOUT, originalError);
151
- return createNetworkError(EC.NETWORK_CONNECTION_FAILED, originalError);
152
- }
153
- return new AppError({ code: fallbackCode, message: originalError.message, cause: originalError });
154
- }
155
- export function toError(error) {
156
- if (error instanceof Error)
157
- return error;
158
- if (typeof error === "string")
159
- return new Error(error);
160
- if (typeof error === "object" && error !== null) {
161
- const message = error.message ||
162
- error.error ||
163
- JSON.stringify(error);
164
- return new Error(String(message));
165
- }
166
- return new Error(String(error));
167
- }
168
- export function extractErrorContext(error) {
169
- const context = { errorCode: error.code, category: error.category };
170
- if (error.cause)
171
- context.originalError = error.cause.message;
172
- if (error.details)
173
- context.details = error.details;
174
- return context;
175
- }
176
- export function getUserMessage(error) {
177
- if (isAppError(error))
178
- return error.userMessage;
179
- if (error instanceof Error)
180
- return "操作失败,请稍后重试";
181
- return "发生未知错误,请稍后重试";
182
- }
183
- export function logError(context, error) {
184
- console.error(`[${error.code}] ${context}:`, error.toLogString());
185
- if (error.cause)
186
- console.error("Original error:", error.cause);
187
- }
188
- //# sourceMappingURL=error-utils.js.map
1
+ import{NoObjectGeneratedError as e}from"ai";import{AppError as r,isAppError as o}from"./app-error.js";import{ErrorCode as s}from"./error-codes.js";import{createLLMError as t,createNetworkError as n,createStructuredOutputError as i}from"./error-factory.js";export function parseAISDKError(e){if(!e||"object"!=typeof e)return null;const r=e;if(!("AI_APICallError"===r.name||"APICallError"===r.name||"string"==typeof r.url&&"number"==typeof r.statusCode))return null;const o="number"==typeof r.statusCode?r.statusCode:void 0,s="string"==typeof r.responseBody?r.responseBody:void 0,t="string"==typeof r.url?r.url:void 0,n=r.message;let i,a;if(t&&(t.includes("openai.com")?i="openai":t.includes("anthropic.com")?i="anthropic":t.includes("dashscope.aliyuncs.com")?i="qwen":t.includes("openrouter.ai")?i="openrouter":t.includes("deepseek.com")?i="deepseek":t.includes("moonshot.cn")?i="moonshotai":t.includes("googleapis.com")&&(i="google")),s){const e=s.match(/model[`'":\s]+([^`'"}\s,]+)/i);e&&(a=e[1])}return{isAuthError:401===o||403===o,isModelNotFound:s?.includes("model")&&s?.includes("not exist")||s?.includes("not authorized to access this model")||!1,isRateLimited:429===o,isTimeout:408===o||504===o||n?.toLowerCase().includes("timeout")||!1,statusCode:o,provider:i,model:a,originalMessage:n,responseBody:s}}function a(e){return[/^```/m,/^#{1,6}\s/m,/^\s*[-*+]\s/m,/^\s*\d+\.\s/m,/\[.+\]\(.+\)/,/^\s*>/m].some(r=>r.test(e))}export function parseNoObjectGeneratedError(r){try{if(void 0===e||"function"!=typeof e.isInstance)return null;if(!e.isInstance(r))return null}catch{return null}const o=r.text,s=!!o&&a(o),t=r.response?{id:r.response.id,timestamp:r.response.timestamp,modelId:r.response.modelId}:void 0,n=r.usage??void 0,i={isNoObjectGeneratedError:!0,isMarkdownFormat:s};return void 0!==o&&(i.rawText=o),r.cause instanceof Error&&(i.cause=r.cause),void 0!==t&&(i.response=t),void 0!==n&&(i.usage=n),i}export function wrapError(e,a=s.SYSTEM_UNKNOWN,u){if(o(e))return u&&u!==e.userMessage?new r({code:e.code,message:e.message,userMessage:u,cause:e.cause,details:e.details}):e;const c=toError(e),d=parseAISDKError(e);if(d){const e={model:d.model,provider:d.provider,statusCode:d.statusCode,responseBody:d.responseBody};return d.isModelNotFound?t(s.LLM_MODEL_NOT_FOUND,c,e):d.isAuthError?t(s.LLM_UNAUTHORIZED,c,e):d.isRateLimited?t(s.LLM_RATE_LIMITED,c,e):d.isTimeout?t(s.LLM_TIMEOUT,c,e):t(s.LLM_GENERATION_FAILED,c,e)}const l=parseNoObjectGeneratedError(e);if(l)return i(s.LLM_RESPONSE_PARSE_ERROR,c,{rawText:l.rawText,isMarkdownFormat:l.isMarkdownFormat,parseErrorMessage:l.cause?.message,usage:l.usage});const m=c.message.toLowerCase();return m.includes("timeout")||m.includes("econnrefused")||m.includes("network")||m.includes("fetch failed")?m.includes("timeout")?n(s.NETWORK_TIMEOUT,c):n(s.NETWORK_CONNECTION_FAILED,c):new r({code:a,message:c.message,cause:c})}export function toError(e){if(e instanceof Error)return e;if("string"==typeof e)return new Error(e);if("object"==typeof e&&null!==e){const r=e.message||e.error||JSON.stringify(e);return new Error(String(r))}return new Error(String(e))}export function extractErrorContext(e){const r={errorCode:e.code,category:e.category};return e.cause&&(r.originalError=e.cause.message),e.details&&(r.details=e.details),r}export function getUserMessage(e){return o(e)?e.userMessage:e instanceof Error?"操作失败,请稍后重试":"发生未知错误,请稍后重试"}export function logError(e,r){console.error(`[${r.code}] ${e}:`,r.toLogString()),r.cause&&console.error("Original error:",r.cause)}
@@ -6,4 +6,3 @@ export { createLLMError, createConfigError, createNetworkError, createValidation
6
6
  export type { LLMErrorContext, StructuredOutputErrorContext } from "./error-factory.ts";
7
7
  export { wrapError, toError, parseAISDKError, parseNoObjectGeneratedError, extractErrorContext, getUserMessage, logError, } from "./error-utils.ts";
8
8
  export type { ErrorContext, NoObjectGeneratedErrorInfo } from "./error-utils.ts";
9
- //# sourceMappingURL=index.d.ts.map
@@ -1,5 +1 @@
1
- export { ErrorCode, ErrorCategory, ERROR_CODE_TO_CATEGORY, ERROR_USER_MESSAGES, getErrorCategory, getErrorUserMessage, isErrorInCategory, } from "./error-codes.js";
2
- export { AppError, isAppError, isErrorCode, isErrorCategory, isLLMError, isConfigError, isNetworkError, isAuthError, } from "./app-error.js";
3
- export { createLLMError, createConfigError, createNetworkError, createValidationError, createBusinessError, createSystemError, createStructuredOutputError, } from "./error-factory.js";
4
- export { wrapError, toError, parseAISDKError, parseNoObjectGeneratedError, extractErrorContext, getUserMessage, logError, } from "./error-utils.js";
5
- //# sourceMappingURL=index.js.map
1
+ export{ErrorCode,ErrorCategory,ERROR_CODE_TO_CATEGORY,ERROR_USER_MESSAGES,getErrorCategory,getErrorUserMessage,isErrorInCategory}from"./error-codes.js";export{AppError,isAppError,isErrorCode,isErrorCategory,isLLMError,isConfigError,isNetworkError,isAuthError}from"./app-error.js";export{createLLMError,createConfigError,createNetworkError,createValidationError,createBusinessError,createSystemError,createStructuredOutputError}from"./error-factory.js";export{wrapError,toError,parseAISDKError,parseNoObjectGeneratedError,extractErrorContext,getUserMessage,logError}from"./error-utils.js";
package/dist/index.d.ts CHANGED
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,12 +1 @@
1
- import { defineAgent } from "@roll-agent/sdk";
2
- import { generateReply } from "./tools/generate-reply.js";
3
- import { syncBrandData } from "./tools/sync-brand-data.js";
4
- const agent = defineAgent({
5
- name: "smart-reply-agent",
6
- tools: [generateReply, syncBrandData],
7
- });
8
- agent.listen().catch((err) => {
9
- console.error("Fatal error:", err);
10
- process.exit(1);
11
- });
12
- //# sourceMappingURL=index.js.map
1
+ import{defineAgent as o}from"@roll-agent/sdk";import{generateReply as r}from"./tools/generate-reply.js";import{syncBrandData as t}from"./tools/sync-brand-data.js";const e=o({name:"smart-reply-agent",tools:[r,t]});e.listen().catch(o=>{console.error("Fatal error:",o),process.exit(1)});
@@ -1,3 +1,2 @@
1
1
  export declare function setSuppressVerboseLogs(value: boolean): void;
2
2
  export declare function verboseLog(...args: unknown[]): void;
3
- //# sourceMappingURL=log-control.d.ts.map
@@ -1,15 +1 @@
1
- /**
2
- * Pipeline 运行期间(spinner 活跃时)抑制中间日志,调试信息改走 diagnostics。
3
- *
4
- * 注意:当前使用模块级标志,仅适用于串行调用场景(stdio MCP)。
5
- * 若需并发安全,应改为 AsyncLocalStorage。
6
- */
7
- let _suppress = false;
8
- export function setSuppressVerboseLogs(value) {
9
- _suppress = value;
10
- }
11
- export function verboseLog(...args) {
12
- if (!_suppress)
13
- console.error(...args);
14
- }
15
- //# sourceMappingURL=log-control.js.map
1
+ let e=!1;export function setSuppressVerboseLogs(o){e=o}export function verboseLog(...o){e||console.error(...o)}
@@ -23,4 +23,3 @@ export declare function evaluateAgeEligibility({ age, brandAlias, cityName, regi
23
23
  regionName?: string | null;
24
24
  strategy?: AgeQualificationPolicy;
25
25
  }): Promise<AgeEligibilityResult>;
26
- //# sourceMappingURL=age-eligibility.d.ts.map
@@ -1,176 +1 @@
1
- import { fetchJobListPage, getDulidayToken, getDulidayJobListEndpoint, extractResults, buildCityCandidates, buildBrandCandidates, } from "../services/duliday-api.js";
2
- export const AGE_ELIGIBILITY_STATUSES = ["pass", "fail", "unknown"];
3
- const fallbackPolicy = {
4
- enabled: false,
5
- revealRange: false,
6
- failStrategy: "礼貌说明不匹配,避免承诺",
7
- unknownStrategy: "先核实年龄或资格条件",
8
- passStrategy: "确认匹配后推进下一步",
9
- allowRedirect: false,
10
- redirectPriority: "low",
11
- };
12
- const JOB_LIST_PAGE_SIZE = 200;
13
- function normalizeText(value) {
14
- return (value ?? "").trim().toLowerCase();
15
- }
16
- function isRecord(value) {
17
- return value !== null && typeof value === "object" && !Array.isArray(value);
18
- }
19
- function firstText(value) {
20
- if (typeof value === "string")
21
- return value;
22
- if (Array.isArray(value)) {
23
- const first = value.find((item) => typeof item === "string");
24
- return typeof first === "string" ? first : "";
25
- }
26
- return "";
27
- }
28
- function pickText(source, keys) {
29
- for (const key of keys) {
30
- if (key in source) {
31
- const text = firstText(source[key]);
32
- if (text)
33
- return text;
34
- }
35
- }
36
- return "";
37
- }
38
- function toNumber(value) {
39
- if (typeof value === "number" && Number.isFinite(value))
40
- return value;
41
- if (typeof value === "string" && value.trim() !== "") {
42
- const num = Number(value);
43
- return Number.isFinite(num) ? num : null;
44
- }
45
- return null;
46
- }
47
- function buildAppliedStrategy(status, policy) {
48
- const effective = policy ?? fallbackPolicy;
49
- const strategy = status === "pass"
50
- ? effective.passStrategy
51
- : status === "fail"
52
- ? effective.failStrategy
53
- : effective.unknownStrategy;
54
- return { ...effective, status, strategy };
55
- }
56
- export async function evaluateAgeEligibility({ age, brandAlias, cityName, regionName, strategy, }) {
57
- const token = getDulidayToken();
58
- const endpoint = getDulidayJobListEndpoint();
59
- const summary = {
60
- minAgeObserved: null,
61
- maxAgeObserved: null,
62
- matchedCount: 0,
63
- total: 0,
64
- };
65
- if (!token) {
66
- return {
67
- status: "unknown",
68
- summary,
69
- appliedStrategy: buildAppliedStrategy("unknown", strategy),
70
- };
71
- }
72
- if (!endpoint) {
73
- return {
74
- status: "unknown",
75
- summary,
76
- appliedStrategy: buildAppliedStrategy("unknown", strategy),
77
- };
78
- }
79
- try {
80
- const payload = await fetchJobListPage(token, endpoint, 1, JOB_LIST_PAGE_SIZE, {
81
- brandAlias: brandAlias ?? null,
82
- cityName: cityName ?? null,
83
- });
84
- const { items, total } = extractResults(payload);
85
- summary.total = total;
86
- const hasAdditionalPages = total > items.length && items.length >= JOB_LIST_PAGE_SIZE;
87
- const brandFilters = buildBrandCandidates(brandAlias, cityName)
88
- .map((c) => normalizeText(c))
89
- .filter(Boolean);
90
- const cityFilters = buildCityCandidates(cityName)
91
- .map((c) => normalizeText(c))
92
- .filter(Boolean);
93
- const regionFilter = normalizeText(regionName);
94
- let anyRange = false;
95
- let anyMatch = false;
96
- for (const item of items) {
97
- if (!isRecord(item))
98
- continue;
99
- const record = item;
100
- const basicInfo = isRecord(record.basicInfo)
101
- ? record.basicInfo
102
- : null;
103
- const storeInfo = basicInfo && isRecord(basicInfo.storeInfo)
104
- ? basicInfo.storeInfo
105
- : null;
106
- const itemBrand = normalizeText(pickText(record, ["brandAlias", "brandName", "brand", "organizationName"]) ||
107
- (basicInfo ? pickText(basicInfo, ["brandAlias", "brandName", "brand"]) : ""));
108
- const itemCity = normalizeText(pickText(record, ["cityName", "storeCityName", "jobCityName"]) ||
109
- (storeInfo ? pickText(storeInfo, ["storeCityName", "cityName"]) : ""));
110
- const itemRegion = normalizeText(pickText(record, ["regionName", "storeRegionName", "districtName"]) ||
111
- (storeInfo ? pickText(storeInfo, ["storeRegionName", "regionName", "districtName"]) : ""));
112
- const itemAddress = normalizeText(pickText(record, ["storeAddress", "jobAddress", "address", "storeExactAddress"]) ||
113
- (storeInfo ? pickText(storeInfo, ["storeAddress", "storeExactAddress", "address"]) : ""));
114
- if (brandFilters.length > 0 &&
115
- !brandFilters.some((f) => itemBrand.includes(f) || f.includes(itemBrand))) {
116
- continue;
117
- }
118
- if (cityFilters.length > 0 && !cityFilters.some((f) => itemCity.includes(f)))
119
- continue;
120
- if (regionFilter &&
121
- !itemRegion.includes(regionFilter) &&
122
- !itemAddress.includes(regionFilter)) {
123
- continue;
124
- }
125
- summary.matchedCount += 1;
126
- const hiringRequirement = isRecord(record.hiringRequirement)
127
- ? record.hiringRequirement
128
- : undefined;
129
- const requirement = isRecord(hiringRequirement?.basicPersonalRequirements)
130
- ? hiringRequirement.basicPersonalRequirements
131
- : undefined;
132
- const minAge = toNumber(requirement?.minAge ?? record.minAge);
133
- const maxAge = toNumber(requirement?.maxAge ?? record.maxAge);
134
- if (minAge !== null) {
135
- anyRange = true;
136
- summary.minAgeObserved =
137
- summary.minAgeObserved === null ? minAge : Math.min(summary.minAgeObserved, minAge);
138
- }
139
- if (maxAge !== null) {
140
- anyRange = true;
141
- summary.maxAgeObserved =
142
- summary.maxAgeObserved === null ? maxAge : Math.max(summary.maxAgeObserved, maxAge);
143
- }
144
- if (typeof age === "number") {
145
- const belowMin = minAge !== null && age < minAge;
146
- const aboveMax = maxAge !== null && age > maxAge;
147
- if (!belowMin && !aboveMax)
148
- anyMatch = true;
149
- }
150
- }
151
- if (typeof age !== "number" || summary.matchedCount === 0 || !anyRange) {
152
- return {
153
- status: "unknown",
154
- summary,
155
- appliedStrategy: buildAppliedStrategy("unknown", strategy),
156
- };
157
- }
158
- if (hasAdditionalPages) {
159
- return {
160
- status: "unknown",
161
- summary,
162
- appliedStrategy: buildAppliedStrategy("unknown", strategy),
163
- };
164
- }
165
- const status = anyMatch ? "pass" : "fail";
166
- return { status, summary, appliedStrategy: buildAppliedStrategy(status, strategy) };
167
- }
168
- catch {
169
- return {
170
- status: "unknown",
171
- summary,
172
- appliedStrategy: buildAppliedStrategy("unknown", strategy),
173
- };
174
- }
175
- }
176
- //# sourceMappingURL=age-eligibility.js.map
1
+ import{fetchJobListPage as e,getDulidayToken as n,getDulidayJobListEndpoint as t,extractResults as r,buildCityCandidates as a,buildBrandCandidates as i}from"../services/duliday-api.js";export const AGE_ELIGIBILITY_STATUSES=["pass","fail","unknown"];const s={enabled:!1,revealRange:!1,failStrategy:"礼貌说明不匹配,避免承诺",unknownStrategy:"先核实年龄或资格条件",passStrategy:"确认匹配后推进下一步",allowRedirect:!1,redirectPriority:"low"},o=200;function u(e){return(e??"").trim().toLowerCase()}function l(e){return null!==e&&"object"==typeof e&&!Array.isArray(e)}function m(e){if("string"==typeof e)return e;if(Array.isArray(e)){const n=e.find(e=>"string"==typeof e);return"string"==typeof n?n:""}return""}function d(e,n){for(const t of n)if(t in e){const n=m(e[t]);if(n)return n}return""}function c(e){if("number"==typeof e&&Number.isFinite(e))return e;if("string"==typeof e&&""!==e.trim()){const n=Number(e);return Number.isFinite(n)?n:null}return null}function f(e,n){const t=n??s,r="pass"===e?t.passStrategy:"fail"===e?t.failStrategy:t.unknownStrategy;return{...t,status:e,strategy:r}}export async function evaluateAgeEligibility({age:s,brandAlias:m,cityName:g,regionName:y,strategy:b}){const p=n(),A=t(),N={minAgeObserved:null,maxAgeObserved:null,matchedCount:0,total:0};if(!p)return{status:"unknown",summary:N,appliedStrategy:f("unknown",b)};if(!A)return{status:"unknown",summary:N,appliedStrategy:f("unknown",b)};try{const n=await e(p,A,1,o,{brandAlias:m??null,cityName:g??null}),{items:t,total:w}=r(n);N.total=w;const S=w>t.length&&t.length>=o,k=i(m,g).map(e=>u(e)).filter(Boolean),v=a(g).map(e=>u(e)).filter(Boolean),h=u(y);let x=!1,I=!1;for(const e of t){if(!l(e))continue;const n=e,t=l(n.basicInfo)?n.basicInfo:null,r=t&&l(t.storeInfo)?t.storeInfo:null,a=u(d(n,["brandAlias","brandName","brand","organizationName"])||(t?d(t,["brandAlias","brandName","brand"]):"")),i=u(d(n,["cityName","storeCityName","jobCityName"])||(r?d(r,["storeCityName","cityName"]):"")),o=u(d(n,["regionName","storeRegionName","districtName"])||(r?d(r,["storeRegionName","regionName","districtName"]):"")),m=u(d(n,["storeAddress","jobAddress","address","storeExactAddress"])||(r?d(r,["storeAddress","storeExactAddress","address"]):""));if(k.length>0&&!k.some(e=>a.includes(e)||e.includes(a)))continue;if(v.length>0&&!v.some(e=>i.includes(e)))continue;if(h&&!o.includes(h)&&!m.includes(h))continue;N.matchedCount+=1;const f=l(n.hiringRequirement)?n.hiringRequirement:void 0,g=l(f?.basicPersonalRequirements)?f.basicPersonalRequirements:void 0,y=c(g?.minAge??n.minAge),b=c(g?.maxAge??n.maxAge);if(null!==y&&(x=!0,N.minAgeObserved=null===N.minAgeObserved?y:Math.min(N.minAgeObserved,y)),null!==b&&(x=!0,N.maxAgeObserved=null===N.maxAgeObserved?b:Math.max(N.maxAgeObserved,b)),"number"==typeof s){null!==y&&s<y||null!==b&&s>b||(I=!0)}}if("number"!=typeof s||0===N.matchedCount||!x)return{status:"unknown",summary:N,appliedStrategy:f("unknown",b)};if(S)return{status:"unknown",summary:N,appliedStrategy:f("unknown",b)};const O=I?"pass":"fail";return{status:O,summary:N,appliedStrategy:f(O,b)}}catch{return{status:"unknown",summary:N,appliedStrategy:f("unknown",b)}}}
@@ -6,4 +6,3 @@ export interface KnownCandidateContext {
6
6
  knownFieldNames: string[];
7
7
  }
8
8
  export declare function buildKnownCandidateContext(candidateInfo?: CandidateInfo): KnownCandidateContext;
9
- //# sourceMappingURL=candidate-context.d.ts.map
@@ -1,31 +1 @@
1
- const CANDIDATE_FIELD_MAP = [
2
- { key: "age", label: "年龄" },
3
- { key: "gender", label: "性别" },
4
- { key: "education", label: "学历" },
5
- { key: "experience", label: "工作经验" },
6
- { key: "expectedSalary", label: "期望薪资" },
7
- { key: "expectedLocation", label: "期望位置" },
8
- { key: "jobAddress", label: "工作地址" },
9
- { key: "healthCertificate", label: "健康证" },
10
- ];
11
- export function buildKnownCandidateContext(candidateInfo) {
12
- if (!candidateInfo)
13
- return { factsText: "", knownFieldNames: [] };
14
- const fields = [];
15
- for (const { key, label } of CANDIDATE_FIELD_MAP) {
16
- const raw = candidateInfo[key];
17
- if (typeof raw === "string" && raw.trim()) {
18
- fields.push({ label, value: raw.trim() });
19
- }
20
- else if (typeof raw === "boolean") {
21
- fields.push({ label, value: raw ? "是" : "否" });
22
- }
23
- }
24
- if (fields.length === 0)
25
- return { factsText: "", knownFieldNames: [] };
26
- return {
27
- factsText: fields.map((f) => `${f.label}:${f.value}`).join("\n"),
28
- knownFieldNames: fields.map((f) => f.label),
29
- };
30
- }
31
- //# sourceMappingURL=candidate-context.js.map
1
+ const e=[{key:"age",label:"年龄"},{key:"gender",label:"性别"},{key:"education",label:"学历"},{key:"experience",label:"工作经验"},{key:"expectedSalary",label:"期望薪资"},{key:"expectedLocation",label:"期望位置"},{key:"jobAddress",label:"工作地址"},{key:"healthCertificate",label:"健康证"}];export function buildKnownCandidateContext(l){if(!l)return{factsText:"",knownFieldNames:[]};const a=[];for(const{key:t,label:n}of e){const e=l[t];"string"==typeof e&&e.trim()?a.push({label:n,value:e.trim()}):"boolean"==typeof e&&a.push({label:n,value:e?"是":"否"})}return 0===a.length?{factsText:"",knownFieldNames:[]}:{factsText:a.map(e=>`${e.label}:${e.value}`).join("\n"),knownFieldNames:a.map(e=>e.label)}}
@@ -3,4 +3,3 @@ import type { CandidateInfo } from "../types/zhipin.ts";
3
3
  export declare function parseAge(ageStr?: string): string | undefined;
4
4
  export declare function resolveCandidateAge(turnPlan: TurnPlan, candidateInfo?: CandidateInfo): number | undefined;
5
5
  export declare function resolveRegionName(turnPlan: TurnPlan, candidateInfo?: CandidateInfo): string | undefined;
6
- //# sourceMappingURL=candidate-utils.d.ts.map
@@ -1,33 +1 @@
1
- export function parseAge(ageStr) {
2
- if (!ageStr)
3
- return undefined;
4
- const match = ageStr.match(/(\d+)/);
5
- return match ? match[1] : ageStr;
6
- }
7
- export function resolveCandidateAge(turnPlan, candidateInfo) {
8
- if (typeof candidateInfo?.age === "number") {
9
- return candidateInfo.age;
10
- }
11
- const parsedAge = parseAge(candidateInfo?.age);
12
- if (parsedAge) {
13
- const age = Number(parsedAge);
14
- if (Number.isFinite(age))
15
- return age;
16
- }
17
- if (typeof turnPlan.extractedInfo.specificAge === "number") {
18
- return turnPlan.extractedInfo.specificAge;
19
- }
20
- return undefined;
21
- }
22
- export function resolveRegionName(turnPlan, candidateInfo) {
23
- const district = turnPlan.extractedInfo.mentionedDistricts?.[0]?.district;
24
- if (district)
25
- return district;
26
- const location = turnPlan.extractedInfo.mentionedLocations?.[0]?.location;
27
- if (location)
28
- return location;
29
- if (candidateInfo?.jobAddress)
30
- return candidateInfo.jobAddress;
31
- return undefined;
32
- }
33
- //# sourceMappingURL=candidate-utils.js.map
1
+ export function parseAge(e){if(!e)return;const t=e.match(/(\d+)/);return t?t[1]:e}export function resolveCandidateAge(e,t){if("number"==typeof t?.age)return t.age;const n=parseAge(t?.age);if(n){const e=Number(n);if(Number.isFinite(e))return e}return"number"==typeof e.extractedInfo.specificAge?e.extractedInfo.specificAge:void 0}export function resolveRegionName(e,t){const n=e.extractedInfo.mentionedDistricts?.[0]?.district;if(n)return n;const r=e.extractedInfo.mentionedLocations?.[0]?.location;return r||(t?.jobAddress?t.jobAddress:void 0)}
@@ -10,4 +10,3 @@ export declare function planTurn(message: string, options: Omit<ClassificationOp
10
10
  replyPolicy?: Pick<ReplyPolicyConfig, "stageGoals">;
11
11
  knownCandidateFields?: string[];
12
12
  }): Promise<TurnPlan>;
13
- //# sourceMappingURL=classification.d.ts.map