Fix ACP task response display parsing
This commit is contained in:
parent
e1e29b393f
commit
11bc09d985
@ -714,15 +714,7 @@ GoTaskServiceResult goTaskServiceResultFromAcpResponse(
|
||||
}
|
||||
return errorText;
|
||||
}();
|
||||
final responseText =
|
||||
(result['output']?.toString().trim().isNotEmpty == true
|
||||
? result['output'].toString().trim()
|
||||
: result['summary']?.toString().trim().isNotEmpty == true
|
||||
? result['summary'].toString().trim()
|
||||
: result['resultSummary']?.toString().trim().isNotEmpty == true
|
||||
? result['resultSummary'].toString().trim()
|
||||
: result['message']?.toString().trim() ?? '')
|
||||
.trim();
|
||||
final responseText = _extractGoTaskDisplayText(result);
|
||||
final primaryText =
|
||||
(completedMessage?.trim().isNotEmpty == true
|
||||
? completedMessage!.trim()
|
||||
@ -748,6 +740,100 @@ GoTaskServiceResult goTaskServiceResultFromAcpResponse(
|
||||
);
|
||||
}
|
||||
|
||||
String _extractGoTaskDisplayText(Object? value, [Set<Object>? visited]) {
|
||||
final seen = visited ?? <Object>{};
|
||||
if (value == null) {
|
||||
return '';
|
||||
}
|
||||
if (value is String) {
|
||||
return value.trim();
|
||||
}
|
||||
if (value is Map) {
|
||||
if (!seen.add(value)) {
|
||||
return '';
|
||||
}
|
||||
final map = value.cast<String, dynamic>();
|
||||
for (final key in const <String>[
|
||||
'output',
|
||||
'summary',
|
||||
'resultSummary',
|
||||
'message',
|
||||
'content',
|
||||
'text',
|
||||
'output_text',
|
||||
]) {
|
||||
final extracted = _extractGoTaskTextCandidate(map[key], seen);
|
||||
if (extracted.isNotEmpty) {
|
||||
return extracted;
|
||||
}
|
||||
}
|
||||
final choices = _castList(map['choices']);
|
||||
if (choices.isNotEmpty) {
|
||||
for (final choice in choices) {
|
||||
final extracted = _extractGoTaskDisplayText(
|
||||
_castMap(choice)['message'],
|
||||
seen,
|
||||
);
|
||||
if (extracted.isNotEmpty) {
|
||||
return extracted;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (final key in const <String>[
|
||||
'result',
|
||||
'payload',
|
||||
'data',
|
||||
'response',
|
||||
'body',
|
||||
]) {
|
||||
final extracted = _extractGoTaskDisplayText(map[key], seen);
|
||||
if (extracted.isNotEmpty) {
|
||||
return extracted;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
if (value is List) {
|
||||
if (!seen.add(value)) {
|
||||
return '';
|
||||
}
|
||||
final parts = <String>[];
|
||||
for (final item in value) {
|
||||
final extracted = _extractGoTaskDisplayText(item, seen);
|
||||
if (extracted.isNotEmpty) {
|
||||
parts.add(extracted);
|
||||
}
|
||||
}
|
||||
return parts.join('\n').trim();
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
String _extractGoTaskTextCandidate(Object? value, Set<Object> visited) {
|
||||
if (value == null) {
|
||||
return '';
|
||||
}
|
||||
if (value is String) {
|
||||
return value.trim();
|
||||
}
|
||||
if (value is List) {
|
||||
return _extractGoTaskDisplayText(value, visited);
|
||||
}
|
||||
if (value is Map) {
|
||||
final map = value.cast<String, dynamic>();
|
||||
final type = map['type']?.toString().trim();
|
||||
if (type == 'output_text') {
|
||||
final text =
|
||||
map['text']?.toString().trim() ?? map['value']?.toString().trim();
|
||||
if (text != null && text.isNotEmpty) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
return _extractGoTaskDisplayText(map, visited);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
Map<String, dynamic> mergeGoTaskServiceResponseResult(
|
||||
Map<String, dynamic> response,
|
||||
Map<String, dynamic> overlay,
|
||||
@ -818,3 +904,10 @@ List<Map<String, dynamic>> _castMapList(Object? raw) {
|
||||
}
|
||||
return raw.map(_castMap).toList(growable: false);
|
||||
}
|
||||
|
||||
List<dynamic> _castList(Object? raw) {
|
||||
if (raw is List) {
|
||||
return raw;
|
||||
}
|
||||
return const <dynamic>[];
|
||||
}
|
||||
|
||||
@ -10,6 +10,64 @@ import 'package:xworkmate/runtime/runtime_models.dart';
|
||||
import 'package:xworkmate/runtime/secure_config_store.dart';
|
||||
|
||||
void main() {
|
||||
group('GoTaskService ACP response parsing', () {
|
||||
test('uses direct bridge output text', () {
|
||||
final result = goTaskServiceResultFromAcpResponse(<String, dynamic>{
|
||||
'jsonrpc': '2.0',
|
||||
'id': 'request-id',
|
||||
'result': <String, dynamic>{
|
||||
'success': true,
|
||||
'output': 'direct response',
|
||||
},
|
||||
}, route: GoTaskServiceRoute.externalAcpSingle);
|
||||
|
||||
expect(result.success, isTrue);
|
||||
expect(result.message, 'direct response');
|
||||
});
|
||||
|
||||
test('uses nested provider result output text', () {
|
||||
final result = goTaskServiceResultFromAcpResponse(<String, dynamic>{
|
||||
'jsonrpc': '2.0',
|
||||
'id': 'request-id',
|
||||
'result': <String, dynamic>{
|
||||
'success': true,
|
||||
'result': <String, dynamic>{
|
||||
'success': true,
|
||||
'output': 'nested provider response',
|
||||
},
|
||||
},
|
||||
}, route: GoTaskServiceRoute.externalAcpSingle);
|
||||
|
||||
expect(result.success, isTrue);
|
||||
expect(result.message, 'nested provider response');
|
||||
});
|
||||
|
||||
test('uses output content list text', () {
|
||||
final result = goTaskServiceResultFromAcpResponse(<String, dynamic>{
|
||||
'jsonrpc': '2.0',
|
||||
'id': 'request-id',
|
||||
'result': <String, dynamic>{
|
||||
'success': true,
|
||||
'payload': <String, dynamic>{
|
||||
'output': <Map<String, dynamic>>[
|
||||
<String, dynamic>{
|
||||
'content': <Map<String, dynamic>>[
|
||||
<String, dynamic>{
|
||||
'type': 'output_text',
|
||||
'text': 'content list response',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}, route: GoTaskServiceRoute.externalAcpSingle);
|
||||
|
||||
expect(result.success, isTrue);
|
||||
expect(result.message, 'content list response');
|
||||
});
|
||||
});
|
||||
|
||||
group('GatewayAcpClient authorization', () {
|
||||
test('normalizes raw resolver token into bearer header for HTTP', () async {
|
||||
final capture = await _startAcpHttpServer();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user