@xfxstudio/claworld 0.2.8 → 0.2.10-beta.0
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/README.md +1 -1
- package/openclaw.plugin.json +7 -63
- package/package.json +6 -2
- package/skills/claworld-help/SKILL.md +7 -3
- package/skills/claworld-join-and-chat/SKILL.md +38 -9
- package/skills/claworld-manage-worlds/SKILL.md +81 -10
- package/src/lib/agent-profile.js +8 -3
- package/src/lib/chat-request.js +19 -1
- package/src/lib/policy.js +2 -6
- package/src/lib/public-identity.js +175 -0
- package/src/lib/relay/kickoff-text.js +7 -1
- package/src/openclaw/installer/cli.js +46 -1
- package/src/openclaw/installer/constants.js +1 -0
- package/src/openclaw/installer/core.js +234 -3
- package/src/openclaw/installer/doctor.js +2 -2
- package/src/openclaw/plugin/account-identity.js +1 -2
- package/src/openclaw/plugin/claworld-channel-plugin.js +302 -266
- package/src/openclaw/plugin/config-schema.js +9 -23
- package/src/openclaw/plugin/managed-config.js +284 -79
- package/src/openclaw/plugin/onboarding.js +22 -42
- package/src/openclaw/plugin/register.js +144 -25
- package/src/openclaw/plugin/relay-client.js +237 -18
- package/src/openclaw/runtime/backend-error-context.js +91 -0
- package/src/openclaw/runtime/feedback-helper.js +1 -2
- package/src/openclaw/runtime/product-shell-helper.js +43 -9
- package/src/openclaw/runtime/tool-contracts.js +65 -3
- package/src/openclaw/runtime/tool-inventory.js +8 -1
- package/src/openclaw/runtime/world-moderation-helper.js +3 -19
- package/src/product-shell/contracts/candidate-feed.js +7 -0
- package/src/product-shell/contracts/world-manifest.js +0 -1
- package/src/product-shell/contracts/world-orchestration.js +10 -1
- package/src/product-shell/conversation-feedback/conversation-feedback-service.js +261 -0
- package/src/product-shell/feedback/feedback-routes.js +0 -1
- package/src/product-shell/feedback/feedback-service.js +4 -9
- package/src/product-shell/index.js +40 -7
- package/src/product-shell/matching/matchmaking-service.js +22 -1
- package/src/product-shell/membership/membership-service.js +5 -1
- package/src/product-shell/onboarding/onboarding-service.js +10 -21
- package/src/product-shell/profile/public-identity-routes.js +60 -0
- package/src/product-shell/profile/public-identity-service.js +190 -0
- package/src/product-shell/search/search-service.js +9 -2
- package/src/product-shell/social/chat-request-routes.js +4 -1
- package/src/product-shell/social/chat-request-service.js +184 -22
- package/src/product-shell/social/friend-routes.js +1 -1
- package/src/product-shell/social/friend-service.js +16 -19
- package/src/product-shell/social/social-routes.js +2 -2
- package/src/product-shell/social/social-service.js +31 -35
- package/src/product-shell/worlds/world-admin-service.js +31 -10
- package/src/product-shell/worlds/world-broadcast-service.js +2 -2
- package/src/lib/agent-address.js +0 -46
|
@@ -85,6 +85,26 @@ function projectWorldStats(stats = null) {
|
|
|
85
85
|
totalParticipants: normalizeOptionalInteger(stats.totalParticipants, null),
|
|
86
86
|
activeParticipants: normalizeOptionalInteger(stats.activeParticipants, null),
|
|
87
87
|
totalConversationCount: normalizeOptionalInteger(stats.totalConversationCount, null),
|
|
88
|
+
totalLikes: normalizeOptionalInteger(stats.totalLikes, null),
|
|
89
|
+
totalDislikes: normalizeOptionalInteger(stats.totalDislikes, null),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function projectWorldFeedbackSummary(summary = null) {
|
|
94
|
+
if (!summary || typeof summary !== 'object' || Array.isArray(summary)) return null;
|
|
95
|
+
return {
|
|
96
|
+
likesReceived: normalizeInteger(summary.likesReceived, 0),
|
|
97
|
+
dislikesReceived: normalizeInteger(summary.dislikesReceived, 0),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function projectConversationFeedbackSummary(summary = null) {
|
|
102
|
+
if (!summary || typeof summary !== 'object' || Array.isArray(summary)) return null;
|
|
103
|
+
return {
|
|
104
|
+
likeCount: normalizeInteger(summary.likeCount, 0),
|
|
105
|
+
dislikeCount: normalizeInteger(summary.dislikeCount, 0),
|
|
106
|
+
viewerGave: normalizeText(summary.viewerGave, null),
|
|
107
|
+
viewerReceived: normalizeText(summary.viewerReceived, null),
|
|
88
108
|
};
|
|
89
109
|
}
|
|
90
110
|
|
|
@@ -254,6 +274,7 @@ function projectToolCandidateSummary(summary = {}, index = 0) {
|
|
|
254
274
|
score: normalizeInteger(summary.score, 0) || null,
|
|
255
275
|
summary: normalizeText(summary.summary, null),
|
|
256
276
|
expiresAt: normalizeText(summary.expiresAt, null),
|
|
277
|
+
worldFeedbackSummary: projectWorldFeedbackSummary(summary.worldFeedbackSummary),
|
|
257
278
|
};
|
|
258
279
|
}
|
|
259
280
|
|
|
@@ -277,6 +298,7 @@ function projectToolCandidateFeed(joinResult = {}) {
|
|
|
277
298
|
score: candidate.score,
|
|
278
299
|
summary: normalizeText(candidate.deliveryReason?.summary, null),
|
|
279
300
|
expiresAt: candidate.expiresAt,
|
|
301
|
+
worldFeedbackSummary: candidate.worldFeedbackSummary,
|
|
280
302
|
}, index))
|
|
281
303
|
: [];
|
|
282
304
|
|
|
@@ -354,6 +376,7 @@ export function projectToolSearchWorldResponse(searchResult = {}, { accountId =
|
|
|
354
376
|
matchReasons: projectSearchMatchReasons(item),
|
|
355
377
|
profileSnippet: summarizeProfileSnippet(item.profileSummary),
|
|
356
378
|
online: item.online === true,
|
|
379
|
+
worldFeedbackSummary: projectWorldFeedbackSummary(item.worldFeedbackSummary),
|
|
357
380
|
}))
|
|
358
381
|
: [];
|
|
359
382
|
|
|
@@ -482,7 +505,7 @@ export function projectToolFeedbackSubmissionResponse(result = {}) {
|
|
|
482
505
|
title: normalizeText(feedback.title, null),
|
|
483
506
|
accountId: normalizeText(feedback.accountId, null),
|
|
484
507
|
reporterAgentId: normalizeText(reporter.agentId, null),
|
|
485
|
-
|
|
508
|
+
reporterIdentity: normalizeText(reporter.publicIdentity?.displayIdentity, null),
|
|
486
509
|
worldId: normalizeText(context.worldId, null),
|
|
487
510
|
conversationKey: normalizeText(context.conversationKey, null),
|
|
488
511
|
turnId: normalizeText(context.turnId, null),
|
|
@@ -503,9 +526,8 @@ function projectToolAgentSummary(agent = {}) {
|
|
|
503
526
|
if (!agent || typeof agent !== 'object') return null;
|
|
504
527
|
return {
|
|
505
528
|
agentId: normalizeText(agent.agentId, null),
|
|
506
|
-
agentCode: normalizeText(agent.agentCode, null),
|
|
507
|
-
address: normalizeText(agent.address, null),
|
|
508
529
|
displayName: normalizeText(agent.displayName, null),
|
|
530
|
+
identity: normalizeText(agent.publicIdentity?.displayIdentity, null),
|
|
509
531
|
online: agent.online === true,
|
|
510
532
|
discoverable: typeof agent.discoverable === 'boolean' ? agent.discoverable : null,
|
|
511
533
|
contactable: typeof agent.contactable === 'boolean' ? agent.contactable : null,
|
|
@@ -622,6 +644,23 @@ function projectChatRequestItem(request = {}) {
|
|
|
622
644
|
};
|
|
623
645
|
}
|
|
624
646
|
|
|
647
|
+
function projectChatInboxChatItem(chat = {}) {
|
|
648
|
+
if (!chat || typeof chat !== 'object' || Array.isArray(chat)) return null;
|
|
649
|
+
return {
|
|
650
|
+
chatRequestId: normalizeText(chat.chatRequestId, null),
|
|
651
|
+
status: normalizeText(chat.status, null),
|
|
652
|
+
direction: normalizeText(chat.direction, null),
|
|
653
|
+
createdAt: normalizeText(chat.createdAt, null),
|
|
654
|
+
updatedAt: normalizeText(chat.updatedAt, null),
|
|
655
|
+
lastTurnAt: normalizeText(chat.lastTurnAt, null),
|
|
656
|
+
conversationKey: normalizeText(chat.conversationKey, null),
|
|
657
|
+
localSessionKey: normalizeText(chat.localSessionKey, normalizeText(chat.sessionKey, null)),
|
|
658
|
+
counterparty: projectToolAgentSummary(chat.counterparty),
|
|
659
|
+
conversation: normalizeConversationScopeDetails(chat.conversation),
|
|
660
|
+
feedbackSummary: projectConversationFeedbackSummary(chat.feedbackSummary),
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
|
|
625
664
|
export function projectToolFriendRequestMutationResponse(result = {}, { accountId = null } = {}) {
|
|
626
665
|
return {
|
|
627
666
|
status: result.alreadyFriends === true
|
|
@@ -690,3 +729,26 @@ export function projectToolChatRequestListResponse(result = {}, { accountId = nu
|
|
|
690
729
|
items,
|
|
691
730
|
};
|
|
692
731
|
}
|
|
732
|
+
|
|
733
|
+
export function projectToolChatInboxResponse(result = {}, { accountId = null } = {}) {
|
|
734
|
+
const pendingRequests = Array.isArray(result.pendingRequests)
|
|
735
|
+
? result.pendingRequests.map((request) => projectChatRequestItem(request)).filter(Boolean)
|
|
736
|
+
: [];
|
|
737
|
+
const chats = Array.isArray(result.chats)
|
|
738
|
+
? result.chats.map((chat) => projectChatInboxChatItem(chat)).filter(Boolean)
|
|
739
|
+
: [];
|
|
740
|
+
return {
|
|
741
|
+
accountId: normalizeText(accountId, null),
|
|
742
|
+
counts: result.counts && typeof result.counts === 'object' && !Array.isArray(result.counts)
|
|
743
|
+
? {
|
|
744
|
+
pendingRequestCount: normalizeInteger(result.counts.pendingRequestCount, pendingRequests.length),
|
|
745
|
+
chatCount: normalizeInteger(result.counts.chatCount, chats.length),
|
|
746
|
+
}
|
|
747
|
+
: {
|
|
748
|
+
pendingRequestCount: pendingRequests.length,
|
|
749
|
+
chatCount: chats.length,
|
|
750
|
+
},
|
|
751
|
+
pendingRequests,
|
|
752
|
+
chats,
|
|
753
|
+
};
|
|
754
|
+
}
|
|
@@ -2,7 +2,7 @@ export const CLAWORLD_TOOL_CONTRACT_VERSION = 'v1';
|
|
|
2
2
|
|
|
3
3
|
export const CLAWORLD_CHAT_REQUEST_TOOL_NAMES = Object.freeze([
|
|
4
4
|
'claworld_request_chat',
|
|
5
|
-
'
|
|
5
|
+
'claworld_chat_inbox',
|
|
6
6
|
'claworld_accept_chat_request',
|
|
7
7
|
'claworld_reject_chat_request',
|
|
8
8
|
]);
|
|
@@ -11,6 +11,11 @@ export const CLAWORLD_BOOTSTRAP_TOOL_NAMES = Object.freeze([
|
|
|
11
11
|
'claworld_pair_agent',
|
|
12
12
|
]);
|
|
13
13
|
|
|
14
|
+
export const CLAWORLD_PROFILE_TOOL_NAMES = Object.freeze([
|
|
15
|
+
'claworld_get_public_identity',
|
|
16
|
+
'claworld_update_public_identity',
|
|
17
|
+
]);
|
|
18
|
+
|
|
14
19
|
export const CLAWORLD_FEEDBACK_TOOL_NAMES = Object.freeze([
|
|
15
20
|
'claworld_submit_feedback',
|
|
16
21
|
]);
|
|
@@ -43,6 +48,7 @@ export const CLAWORLD_RETIRED_PUBLIC_TOOL_NAMES = Object.freeze([
|
|
|
43
48
|
|
|
44
49
|
export const CLAWORLD_REGISTERED_TOOL_NAMES = Object.freeze([
|
|
45
50
|
...CLAWORLD_BOOTSTRAP_TOOL_NAMES,
|
|
51
|
+
...CLAWORLD_PROFILE_TOOL_NAMES,
|
|
46
52
|
...CLAWORLD_WORLD_TOOL_NAMES,
|
|
47
53
|
...CLAWORLD_WORLD_ADMIN_PUBLIC_TOOL_NAMES,
|
|
48
54
|
...CLAWORLD_CHAT_REQUEST_TOOL_NAMES,
|
|
@@ -67,6 +73,7 @@ export const CLAWORLD_READ_ONLY_OPENCLAW_TOOL_NAMES = Object.freeze([
|
|
|
67
73
|
|
|
68
74
|
export const CLAWORLD_PLUGIN_SMOKE_REQUIRED_TOOL_NAMES = Object.freeze([
|
|
69
75
|
...CLAWORLD_BOOTSTRAP_TOOL_NAMES,
|
|
76
|
+
...CLAWORLD_PROFILE_TOOL_NAMES,
|
|
70
77
|
...CLAWORLD_WORLD_TOOL_NAMES,
|
|
71
78
|
...CLAWORLD_WORLD_ADMIN_PUBLIC_TOOL_NAMES,
|
|
72
79
|
...CLAWORLD_CHAT_REQUEST_TOOL_NAMES,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resolveClaworldRuntimeConfig } from '../plugin/config-schema.js';
|
|
2
2
|
import { buildRuntimeAuthHeaders } from '../plugin/account-identity.js';
|
|
3
3
|
import { createRuntimeBoundaryError } from '../../lib/runtime-errors.js';
|
|
4
|
+
import { extractBackendErrorContext } from './backend-error-context.js';
|
|
4
5
|
|
|
5
6
|
function normalizeText(value, fallback = null) {
|
|
6
7
|
if (value == null) return fallback;
|
|
@@ -36,18 +37,6 @@ function normalizeStringList(values = []) {
|
|
|
36
37
|
return [...new Set(values.map((value) => normalizeText(value, null)).filter(Boolean))];
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
function normalizeFieldError(fieldError = {}) {
|
|
40
|
-
const fieldId = normalizeText(fieldError.fieldId, null);
|
|
41
|
-
const message = normalizeText(fieldError.message, null);
|
|
42
|
-
const code = normalizeText(fieldError.code, null);
|
|
43
|
-
if (!fieldId && !message && !code) return null;
|
|
44
|
-
return {
|
|
45
|
-
...(fieldId ? { fieldId } : {}),
|
|
46
|
-
...(message ? { message } : {}),
|
|
47
|
-
...(code ? { code } : {}),
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
40
|
function normalizeParticipantContextField(field = {}, index = 0) {
|
|
52
41
|
return {
|
|
53
42
|
fieldId: normalizeText(field.fieldId, `field_${index + 1}`),
|
|
@@ -160,9 +149,6 @@ function inferHttpErrorCategory(status) {
|
|
|
160
149
|
function createModerationHttpError(action, response, { accountId = null, worldId = null } = {}) {
|
|
161
150
|
const backendCode = normalizeText(response?.body?.error, null);
|
|
162
151
|
const backendMessage = normalizeText(response?.body?.message, `claworld world ${action} failed`);
|
|
163
|
-
const fieldErrors = Array.isArray(response?.body?.fieldErrors)
|
|
164
|
-
? response.body.fieldErrors.map((fieldError) => normalizeFieldError(fieldError)).filter(Boolean)
|
|
165
|
-
: [];
|
|
166
152
|
|
|
167
153
|
return createRuntimeBoundaryError({
|
|
168
154
|
code: backendCode || `claworld_world_${action}_failed`,
|
|
@@ -176,9 +162,7 @@ function createModerationHttpError(action, response, { accountId = null, worldId
|
|
|
176
162
|
accountId,
|
|
177
163
|
...(worldId ? { worldId } : {}),
|
|
178
164
|
httpStatus: response?.status ?? 500,
|
|
179
|
-
|
|
180
|
-
backendMessage,
|
|
181
|
-
...(fieldErrors.length > 0 ? { fieldErrors } : {}),
|
|
165
|
+
...extractBackendErrorContext(response?.body),
|
|
182
166
|
},
|
|
183
167
|
});
|
|
184
168
|
}
|
|
@@ -190,7 +174,7 @@ export async function createModeratedWorld({
|
|
|
190
174
|
agentId = null,
|
|
191
175
|
displayName = null,
|
|
192
176
|
worldContextText = null,
|
|
193
|
-
enabled =
|
|
177
|
+
enabled = true,
|
|
194
178
|
fetchImpl,
|
|
195
179
|
logger = console,
|
|
196
180
|
} = {}) {
|
|
@@ -12,9 +12,15 @@ export const CANDIDATE_OBJECT_FIELDS = Object.freeze([
|
|
|
12
12
|
'profileSummary',
|
|
13
13
|
'compatibilitySignals',
|
|
14
14
|
'deliveryReason',
|
|
15
|
+
'worldFeedbackSummary',
|
|
15
16
|
'expiresAt',
|
|
16
17
|
]);
|
|
17
18
|
|
|
19
|
+
export const WORLD_FEEDBACK_SUMMARY_FIELDS = Object.freeze([
|
|
20
|
+
'likesReceived',
|
|
21
|
+
'dislikesReceived',
|
|
22
|
+
]);
|
|
23
|
+
|
|
18
24
|
export const DATING_DEMO_SCORING_FIELDS = Object.freeze([
|
|
19
25
|
'joinedAt',
|
|
20
26
|
'rank',
|
|
@@ -282,6 +288,7 @@ export function projectCandidateFeedModel(world) {
|
|
|
282
288
|
profileSummaryFieldShape: PROFILE_SUMMARY_FIELD_FIELDS,
|
|
283
289
|
compatibilitySignalFields: COMPATIBILITY_SIGNAL_FIELDS,
|
|
284
290
|
deliveryReasonFields: DELIVERY_REASON_FIELDS,
|
|
291
|
+
worldFeedbackSummaryFields: WORLD_FEEDBACK_SUMMARY_FIELDS,
|
|
285
292
|
requestChatAction: {
|
|
286
293
|
action: 'request_chat',
|
|
287
294
|
requiredFields: ['worldId', 'targetAgentId'],
|
|
@@ -390,7 +390,12 @@ function summarizeProfileFields(fields = []) {
|
|
|
390
390
|
}
|
|
391
391
|
|
|
392
392
|
function buildCandidateDeliverySummaryLine(candidateSummary = {}, index = 0) {
|
|
393
|
-
|
|
393
|
+
const likesReceived = Number(candidateSummary.worldFeedbackSummary?.likesReceived || 0);
|
|
394
|
+
const dislikesReceived = Number(candidateSummary.worldFeedbackSummary?.dislikesReceived || 0);
|
|
395
|
+
const feedbackLine = likesReceived > 0 || dislikesReceived > 0
|
|
396
|
+
? ` World feedback in this world: ${likesReceived} like${likesReceived === 1 ? '' : 's'}, ${dislikesReceived} dislike${dislikesReceived === 1 ? '' : 's'}.`
|
|
397
|
+
: '';
|
|
398
|
+
return `${index + 1}. ${candidateSummary.summary}${feedbackLine}`;
|
|
394
399
|
}
|
|
395
400
|
|
|
396
401
|
function formatConversationOverview(detail = {}) {
|
|
@@ -697,6 +702,10 @@ export function buildCandidateDeliverySummary(candidateFeed = {}, { worldDetail
|
|
|
697
702
|
optionalFieldSummary,
|
|
698
703
|
compatibilitySummary,
|
|
699
704
|
deliveryReasonSummary: deliveryReasonSummary || null,
|
|
705
|
+
worldFeedbackSummary: candidate.worldFeedbackSummary || {
|
|
706
|
+
likesReceived: 0,
|
|
707
|
+
dislikesReceived: 0,
|
|
708
|
+
},
|
|
700
709
|
expiresAt: candidate.expiresAt,
|
|
701
710
|
summary,
|
|
702
711
|
};
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
const FEEDBACK_TOKEN_TO_SENTIMENT = Object.freeze({
|
|
2
|
+
'[[like]]': 'like',
|
|
3
|
+
'[[dislike]]': 'dislike',
|
|
4
|
+
});
|
|
5
|
+
|
|
6
|
+
const SENTIMENT_VALUES = Object.freeze(['like', 'dislike']);
|
|
7
|
+
|
|
8
|
+
function normalizeText(value, fallback = null) {
|
|
9
|
+
if (value == null) return fallback;
|
|
10
|
+
const normalized = String(value).trim();
|
|
11
|
+
return normalized || fallback;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function createConfigurationError() {
|
|
15
|
+
const error = new Error('conversation_feedback_store_unavailable');
|
|
16
|
+
error.code = 'conversation_feedback_store_unavailable';
|
|
17
|
+
error.status = 500;
|
|
18
|
+
return error;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function createInvalidConversationFeedbackError(fieldId, message = `${fieldId} is required`) {
|
|
22
|
+
const error = new Error(`invalid_conversation_feedback:${fieldId}`);
|
|
23
|
+
error.code = 'invalid_conversation_feedback';
|
|
24
|
+
error.status = 400;
|
|
25
|
+
error.responseBody = {
|
|
26
|
+
error: error.code,
|
|
27
|
+
message: 'conversation feedback request is invalid',
|
|
28
|
+
fieldErrors: [
|
|
29
|
+
{
|
|
30
|
+
fieldId,
|
|
31
|
+
message,
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
return error;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function normalizeSentiment(value, fallback = null) {
|
|
39
|
+
const normalized = normalizeText(value, fallback);
|
|
40
|
+
return SENTIMENT_VALUES.includes(normalized) ? normalized : fallback;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function resolveSentimentFromToken(token = null) {
|
|
44
|
+
const normalizedToken = normalizeText(token, null);
|
|
45
|
+
return normalizedToken ? FEEDBACK_TOKEN_TO_SENTIMENT[normalizedToken] || null : null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function parseConversationFeedbackTokens(text = '') {
|
|
49
|
+
if (typeof text !== 'string' || !text.trim()) {
|
|
50
|
+
return {
|
|
51
|
+
tokens: [],
|
|
52
|
+
sentiment: null,
|
|
53
|
+
conflict: false,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const matches = [...text.matchAll(/\[\[(like|dislike)\]\]/g)];
|
|
58
|
+
if (matches.length === 0) {
|
|
59
|
+
return {
|
|
60
|
+
tokens: [],
|
|
61
|
+
sentiment: null,
|
|
62
|
+
conflict: false,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const tokens = [...new Set(matches.map((match) => `[[${String(match[1] || '').trim().toLowerCase()}]]`))];
|
|
67
|
+
const sentiments = [...new Set(tokens.map((token) => resolveSentimentFromToken(token)).filter(Boolean))];
|
|
68
|
+
return {
|
|
69
|
+
tokens,
|
|
70
|
+
sentiment: sentiments.length === 1 ? sentiments[0] : null,
|
|
71
|
+
conflict: sentiments.length > 1,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function projectFeedbackMark(feedbackMark = {}) {
|
|
76
|
+
return {
|
|
77
|
+
feedbackMarkId: normalizeText(feedbackMark.feedbackMarkId, null),
|
|
78
|
+
conversationKey: normalizeText(feedbackMark.conversationKey, null),
|
|
79
|
+
worldId: normalizeText(feedbackMark.worldId, null),
|
|
80
|
+
giverAgentId: normalizeText(feedbackMark.giverAgentId, null),
|
|
81
|
+
receiverAgentId: normalizeText(feedbackMark.receiverAgentId, null),
|
|
82
|
+
sourceTurnId: normalizeText(feedbackMark.sourceTurnId, null),
|
|
83
|
+
sourceDeliveryId: normalizeText(feedbackMark.sourceDeliveryId, null),
|
|
84
|
+
token: normalizeText(feedbackMark.token, null),
|
|
85
|
+
sentiment: normalizeSentiment(feedbackMark.sentiment, null),
|
|
86
|
+
source: normalizeText(feedbackMark.source, 'reply_control_token'),
|
|
87
|
+
status: normalizeText(feedbackMark.status, 'active'),
|
|
88
|
+
createdAt: normalizeText(feedbackMark.createdAt, null),
|
|
89
|
+
updatedAt: normalizeText(feedbackMark.updatedAt, normalizeText(feedbackMark.createdAt, null)),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function summarizeReceived(items = []) {
|
|
94
|
+
return {
|
|
95
|
+
likesReceived: items.filter((item) => item.sentiment === 'like').length,
|
|
96
|
+
dislikesReceived: items.filter((item) => item.sentiment === 'dislike').length,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function createConversationFeedbackService({ store } = {}) {
|
|
101
|
+
if (
|
|
102
|
+
!store
|
|
103
|
+
|| typeof store.createConversationFeedbackMark !== 'function'
|
|
104
|
+
|| typeof store.listConversationFeedbackMarks !== 'function'
|
|
105
|
+
|| typeof store.findConversationFeedbackMarkByConversationAndActors !== 'function'
|
|
106
|
+
) {
|
|
107
|
+
throw createConfigurationError();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
parseTokens(text = '') {
|
|
112
|
+
return parseConversationFeedbackTokens(text);
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
async recordFeedback({
|
|
116
|
+
conversationKey,
|
|
117
|
+
worldId = null,
|
|
118
|
+
giverAgentId,
|
|
119
|
+
receiverAgentId,
|
|
120
|
+
sourceTurnId = null,
|
|
121
|
+
sourceDeliveryId = null,
|
|
122
|
+
token = null,
|
|
123
|
+
sentiment = null,
|
|
124
|
+
} = {}) {
|
|
125
|
+
const normalizedConversationKey = normalizeText(conversationKey, null);
|
|
126
|
+
if (!normalizedConversationKey) {
|
|
127
|
+
throw createInvalidConversationFeedbackError('conversationKey');
|
|
128
|
+
}
|
|
129
|
+
const normalizedGiverAgentId = normalizeText(giverAgentId, null);
|
|
130
|
+
if (!normalizedGiverAgentId) {
|
|
131
|
+
throw createInvalidConversationFeedbackError('giverAgentId');
|
|
132
|
+
}
|
|
133
|
+
const normalizedReceiverAgentId = normalizeText(receiverAgentId, null);
|
|
134
|
+
if (!normalizedReceiverAgentId) {
|
|
135
|
+
throw createInvalidConversationFeedbackError('receiverAgentId');
|
|
136
|
+
}
|
|
137
|
+
const normalizedToken = normalizeText(token, null);
|
|
138
|
+
const normalizedSentiment = normalizeSentiment(sentiment, resolveSentimentFromToken(normalizedToken));
|
|
139
|
+
if (!normalizedSentiment) {
|
|
140
|
+
throw createInvalidConversationFeedbackError('sentiment', 'sentiment must be like or dislike');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const existing = store.findConversationFeedbackMarkByConversationAndActors({
|
|
144
|
+
conversationKey: normalizedConversationKey,
|
|
145
|
+
giverAgentId: normalizedGiverAgentId,
|
|
146
|
+
receiverAgentId: normalizedReceiverAgentId,
|
|
147
|
+
});
|
|
148
|
+
if (existing) {
|
|
149
|
+
return {
|
|
150
|
+
recorded: false,
|
|
151
|
+
deduped: true,
|
|
152
|
+
feedbackMark: projectFeedbackMark(existing),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const feedbackMark = await store.createConversationFeedbackMark({
|
|
157
|
+
conversationKey: normalizedConversationKey,
|
|
158
|
+
worldId: normalizeText(worldId, null),
|
|
159
|
+
giverAgentId: normalizedGiverAgentId,
|
|
160
|
+
receiverAgentId: normalizedReceiverAgentId,
|
|
161
|
+
sourceTurnId: normalizeText(sourceTurnId, null),
|
|
162
|
+
sourceDeliveryId: normalizeText(sourceDeliveryId, null),
|
|
163
|
+
token: normalizedToken || `[[${normalizedSentiment}]]`,
|
|
164
|
+
sentiment: normalizedSentiment,
|
|
165
|
+
source: 'reply_control_token',
|
|
166
|
+
status: 'active',
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
recorded: true,
|
|
171
|
+
deduped: false,
|
|
172
|
+
feedbackMark: projectFeedbackMark(feedbackMark),
|
|
173
|
+
};
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
listFeedbackMarks(filters = {}) {
|
|
177
|
+
return store.listConversationFeedbackMarks(filters).map((item) => projectFeedbackMark(item));
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
summarizeConversation({ conversationKey, viewerAgentId = null } = {}) {
|
|
181
|
+
const normalizedConversationKey = normalizeText(conversationKey, null);
|
|
182
|
+
const normalizedViewerAgentId = normalizeText(viewerAgentId, null);
|
|
183
|
+
const items = normalizedConversationKey
|
|
184
|
+
? this.listFeedbackMarks({ conversationKey: normalizedConversationKey, status: 'active' })
|
|
185
|
+
: [];
|
|
186
|
+
const viewerGave = normalizedViewerAgentId
|
|
187
|
+
? items.find((item) => item.giverAgentId === normalizedViewerAgentId)?.sentiment || null
|
|
188
|
+
: null;
|
|
189
|
+
const viewerReceived = normalizedViewerAgentId
|
|
190
|
+
? items.find((item) => item.receiverAgentId === normalizedViewerAgentId)?.sentiment || null
|
|
191
|
+
: null;
|
|
192
|
+
return {
|
|
193
|
+
likeCount: items.filter((item) => item.sentiment === 'like').length,
|
|
194
|
+
dislikeCount: items.filter((item) => item.sentiment === 'dislike').length,
|
|
195
|
+
viewerGave,
|
|
196
|
+
viewerReceived,
|
|
197
|
+
};
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
summarizeAgent({ agentId } = {}) {
|
|
201
|
+
const normalizedAgentId = normalizeText(agentId, null);
|
|
202
|
+
if (!normalizedAgentId) {
|
|
203
|
+
return {
|
|
204
|
+
totalLikesReceived: 0,
|
|
205
|
+
totalDislikesReceived: 0,
|
|
206
|
+
totalLikesGiven: 0,
|
|
207
|
+
totalDislikesGiven: 0,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
const received = this.listFeedbackMarks({
|
|
211
|
+
receiverAgentId: normalizedAgentId,
|
|
212
|
+
status: 'active',
|
|
213
|
+
});
|
|
214
|
+
const given = this.listFeedbackMarks({
|
|
215
|
+
giverAgentId: normalizedAgentId,
|
|
216
|
+
status: 'active',
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
totalLikesReceived: received.filter((item) => item.sentiment === 'like').length,
|
|
221
|
+
totalDislikesReceived: received.filter((item) => item.sentiment === 'dislike').length,
|
|
222
|
+
totalLikesGiven: given.filter((item) => item.sentiment === 'like').length,
|
|
223
|
+
totalDislikesGiven: given.filter((item) => item.sentiment === 'dislike').length,
|
|
224
|
+
};
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
summarizeWorldAgent({ worldId, agentId } = {}) {
|
|
228
|
+
const normalizedWorldId = normalizeText(worldId, null);
|
|
229
|
+
const normalizedAgentId = normalizeText(agentId, null);
|
|
230
|
+
if (!normalizedWorldId || !normalizedAgentId) {
|
|
231
|
+
return {
|
|
232
|
+
likesReceived: 0,
|
|
233
|
+
dislikesReceived: 0,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
return summarizeReceived(this.listFeedbackMarks({
|
|
237
|
+
worldId: normalizedWorldId,
|
|
238
|
+
receiverAgentId: normalizedAgentId,
|
|
239
|
+
status: 'active',
|
|
240
|
+
}));
|
|
241
|
+
},
|
|
242
|
+
|
|
243
|
+
summarizeWorld({ worldId } = {}) {
|
|
244
|
+
const normalizedWorldId = normalizeText(worldId, null);
|
|
245
|
+
if (!normalizedWorldId) {
|
|
246
|
+
return {
|
|
247
|
+
totalLikes: 0,
|
|
248
|
+
totalDislikes: 0,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
const items = this.listFeedbackMarks({
|
|
252
|
+
worldId: normalizedWorldId,
|
|
253
|
+
status: 'active',
|
|
254
|
+
});
|
|
255
|
+
return {
|
|
256
|
+
totalLikes: items.filter((item) => item.sentiment === 'like').length,
|
|
257
|
+
totalDislikes: items.filter((item) => item.sentiment === 'dislike').length,
|
|
258
|
+
};
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
}
|
|
@@ -57,7 +57,6 @@ export function registerFeedbackRoutes(app, { store, feedbackService }) {
|
|
|
57
57
|
turnId: req.body?.turnId,
|
|
58
58
|
deliveryId: req.body?.deliveryId,
|
|
59
59
|
targetAgentId: req.body?.targetAgentId,
|
|
60
|
-
targetAgentCode: req.body?.targetAgentCode,
|
|
61
60
|
tags: req.body?.tags,
|
|
62
61
|
metadata: req.body?.metadata,
|
|
63
62
|
context: req.body?.context,
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
FEEDBACK_CATEGORY_VALUES,
|
|
3
3
|
FEEDBACK_IMPACT_VALUES,
|
|
4
4
|
} from './feedback-contract.js';
|
|
5
|
+
import { resolvePublicIdentity } from '../../lib/public-identity.js';
|
|
5
6
|
|
|
6
7
|
function normalizeText(value, fallback = null) {
|
|
7
8
|
if (value == null) return fallback;
|
|
@@ -111,13 +112,11 @@ function projectFeedback(feedback = {}) {
|
|
|
111
112
|
reporter: feedback.reporter && typeof feedback.reporter === 'object'
|
|
112
113
|
? {
|
|
113
114
|
agentId: feedback.reporter.agentId || null,
|
|
114
|
-
|
|
115
|
-
address: feedback.reporter.address || null,
|
|
115
|
+
publicIdentity: feedback.reporter.publicIdentity || null,
|
|
116
116
|
}
|
|
117
117
|
: {
|
|
118
118
|
agentId: null,
|
|
119
|
-
|
|
120
|
-
address: null,
|
|
119
|
+
publicIdentity: null,
|
|
121
120
|
},
|
|
122
121
|
context: feedback.context && typeof feedback.context === 'object'
|
|
123
122
|
? {
|
|
@@ -126,7 +125,6 @@ function projectFeedback(feedback = {}) {
|
|
|
126
125
|
turnId: feedback.context.turnId || null,
|
|
127
126
|
deliveryId: feedback.context.deliveryId || null,
|
|
128
127
|
targetAgentId: feedback.context.targetAgentId || null,
|
|
129
|
-
targetAgentCode: feedback.context.targetAgentCode || null,
|
|
130
128
|
tags: Array.isArray(feedback.context.tags) ? feedback.context.tags : [],
|
|
131
129
|
metadata: feedback.context.metadata && typeof feedback.context.metadata === 'object'
|
|
132
130
|
? feedback.context.metadata
|
|
@@ -138,7 +136,6 @@ function projectFeedback(feedback = {}) {
|
|
|
138
136
|
turnId: null,
|
|
139
137
|
deliveryId: null,
|
|
140
138
|
targetAgentId: null,
|
|
141
|
-
targetAgentCode: null,
|
|
142
139
|
tags: [],
|
|
143
140
|
metadata: {},
|
|
144
141
|
},
|
|
@@ -178,8 +175,7 @@ export function createFeedbackService({ store } = {}) {
|
|
|
178
175
|
accountId: normalizeText(input.accountId, null),
|
|
179
176
|
reporter: {
|
|
180
177
|
agentId: reporter.agentId,
|
|
181
|
-
|
|
182
|
-
address: reporter.address,
|
|
178
|
+
publicIdentity: resolvePublicIdentity(reporter),
|
|
183
179
|
},
|
|
184
180
|
context: {
|
|
185
181
|
worldId: normalizeText(input.worldId, normalizeText(context.worldId, null)),
|
|
@@ -187,7 +183,6 @@ export function createFeedbackService({ store } = {}) {
|
|
|
187
183
|
turnId: normalizeText(input.turnId, normalizeText(context.turnId, null)),
|
|
188
184
|
deliveryId: normalizeText(input.deliveryId, normalizeText(context.deliveryId, null)),
|
|
189
185
|
targetAgentId: normalizeText(input.targetAgentId, normalizeText(context.targetAgentId, null)),
|
|
190
|
-
targetAgentCode: normalizeText(input.targetAgentCode, normalizeText(context.targetAgentCode, null)),
|
|
191
186
|
tags: normalizeStringList(input.tags || context.tags),
|
|
192
187
|
metadata: normalizePlainObject(input.metadata && Object.keys(input.metadata || {}).length > 0 ? input.metadata : context.metadata),
|
|
193
188
|
},
|