feat: Integrate backend list agents endpoint
Backend team successfully fixed Swagger conflicts and implemented simple GET endpoints. Frontend now integrates with GET /api/agents to list all agents. Changes: - agent_endpoint.dart: - Added fromInt() methods to all enums (AgentType, AgentStatus, ModelProviderType) - Updated AgentDto.fromJson() to handle integer enum values from backend - Added listAgents() method using HTTP GET /api/agents - Added imports: dart:async, dart:convert, dart:io, package:http - agents_page.dart: - Updated _loadAgents() to call listAgents() API method - Removed placeholder delay, now uses real data from backend - Removed unused getwidget import Backend Integration: ✅ Backend returns 5 test agents (seeded successfully) ✅ Enums transmitted as integers (CodeGenerator=0, Active=0, etc.) ✅ Frontend properly parses integer enums to Dart enum types ✅ GET /api/agents endpoint working and tested ✅ Full CRUD cycle now functional Testing: - Flutter analyze: 0 errors, 0 warnings - Backend health check: ✅ passing - List endpoint: ✅ returns 5 agents - App running: http://localhost:8080 Phase 2 Complete: Frontend can now display agents from backend! 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
f5a5c5697c
commit
30575c6f77
@ -1,6 +1,12 @@
|
|||||||
/// Agent management endpoints for CQRS API
|
/// Agent management endpoints for CQRS API
|
||||||
library;
|
library;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
import '../client.dart';
|
import '../client.dart';
|
||||||
import '../types.dart';
|
import '../types.dart';
|
||||||
|
|
||||||
@ -25,6 +31,15 @@ enum AgentType {
|
|||||||
orElse: () => AgentType.custom,
|
orElse: () => AgentType.custom,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert from integer value (backend enum representation)
|
||||||
|
/// Backend: CodeGenerator=0, CodeReviewer=1, Debugger=2, Documenter=3, Custom=4
|
||||||
|
static AgentType fromInt(int value) {
|
||||||
|
if (value >= 0 && value < AgentType.values.length) {
|
||||||
|
return AgentType.values[value];
|
||||||
|
}
|
||||||
|
return AgentType.custom;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents the current status of an agent
|
/// Represents the current status of an agent
|
||||||
@ -42,6 +57,15 @@ enum AgentStatus {
|
|||||||
orElse: () => AgentStatus.inactive,
|
orElse: () => AgentStatus.inactive,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert from integer value (backend enum representation)
|
||||||
|
/// Backend: Active=0, Inactive=1, Error=2
|
||||||
|
static AgentStatus fromInt(int value) {
|
||||||
|
if (value >= 0 && value < AgentStatus.values.length) {
|
||||||
|
return AgentStatus.values[value];
|
||||||
|
}
|
||||||
|
return AgentStatus.inactive;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specifies the type of model provider (cloud API or local endpoint)
|
/// Specifies the type of model provider (cloud API or local endpoint)
|
||||||
@ -59,6 +83,15 @@ enum ModelProviderType {
|
|||||||
orElse: () => ModelProviderType.custom,
|
orElse: () => ModelProviderType.custom,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert from integer value (backend enum representation)
|
||||||
|
/// Backend: CloudApi=0, LocalEndpoint=1, Custom=2
|
||||||
|
static ModelProviderType fromInt(int value) {
|
||||||
|
if (value >= 0 && value < ModelProviderType.values.length) {
|
||||||
|
return ModelProviderType.values[value];
|
||||||
|
}
|
||||||
|
return ModelProviderType.custom;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
@ -239,21 +272,40 @@ class AgentDto {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory AgentDto.fromJson(Map<String, Object?> json) {
|
factory AgentDto.fromJson(Map<String, Object?> json) {
|
||||||
|
// Helper to parse enum from either int or string
|
||||||
|
AgentType parseAgentType(Object? value) {
|
||||||
|
if (value is int) return AgentType.fromInt(value);
|
||||||
|
if (value is String) return AgentType.fromString(value);
|
||||||
|
return AgentType.custom;
|
||||||
|
}
|
||||||
|
|
||||||
|
AgentStatus parseAgentStatus(Object? value) {
|
||||||
|
if (value is int) return AgentStatus.fromInt(value);
|
||||||
|
if (value is String) return AgentStatus.fromString(value);
|
||||||
|
return AgentStatus.inactive;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelProviderType parseProviderType(Object? value) {
|
||||||
|
if (value is int) return ModelProviderType.fromInt(value);
|
||||||
|
if (value is String) return ModelProviderType.fromString(value);
|
||||||
|
return ModelProviderType.custom;
|
||||||
|
}
|
||||||
|
|
||||||
return AgentDto(
|
return AgentDto(
|
||||||
id: json['id'] as String,
|
id: json['id'] as String,
|
||||||
name: json['name'] as String,
|
name: json['name'] as String,
|
||||||
description: json['description'] as String,
|
description: json['description'] as String,
|
||||||
type: AgentType.fromString(json['type'] as String),
|
type: parseAgentType(json['type']),
|
||||||
modelProvider: json['modelProvider'] as String,
|
modelProvider: json['modelProvider'] as String,
|
||||||
modelName: json['modelName'] as String,
|
modelName: json['modelName'] as String,
|
||||||
providerType: ModelProviderType.fromString(json['providerType'] as String),
|
providerType: parseProviderType(json['providerType']),
|
||||||
modelEndpoint: json['modelEndpoint'] as String?,
|
modelEndpoint: json['modelEndpoint'] as String?,
|
||||||
temperature: (json['temperature'] as num).toDouble(),
|
temperature: (json['temperature'] as num).toDouble(),
|
||||||
maxTokens: json['maxTokens'] as int,
|
maxTokens: json['maxTokens'] as int,
|
||||||
systemPrompt: json['systemPrompt'] as String,
|
systemPrompt: json['systemPrompt'] as String,
|
||||||
enableMemory: json['enableMemory'] as bool,
|
enableMemory: json['enableMemory'] as bool,
|
||||||
conversationWindowSize: json['conversationWindowSize'] as int,
|
conversationWindowSize: json['conversationWindowSize'] as int,
|
||||||
status: AgentStatus.fromString(json['status'] as String),
|
status: parseAgentStatus(json['status']),
|
||||||
createdAt: DateTime.parse(json['createdAt'] as String),
|
createdAt: DateTime.parse(json['createdAt'] as String),
|
||||||
updatedAt: DateTime.parse(json['updatedAt'] as String),
|
updatedAt: DateTime.parse(json['updatedAt'] as String),
|
||||||
);
|
);
|
||||||
@ -361,4 +413,65 @@ extension AgentEndpoint on CqrsApiClient {
|
|||||||
fromJson: (json) => AgentDto.fromJson(json as Map<String, Object?>),
|
fromJson: (json) => AgentDto.fromJson(json as Map<String, Object?>),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// List all agents
|
||||||
|
///
|
||||||
|
/// Returns a list of all active agents from the backend.
|
||||||
|
/// Backend endpoint: GET /api/agents
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```dart
|
||||||
|
/// final result = await client.listAgents();
|
||||||
|
///
|
||||||
|
/// result.when(
|
||||||
|
/// success: (agents) => print('Found ${agents.length} agents'),
|
||||||
|
/// error: (error) => print('Error: ${error.message}'),
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
Future<Result<List<AgentDto>>> listAgents() async {
|
||||||
|
try {
|
||||||
|
final Uri url = Uri.parse('${config.baseUrl}/api/agents');
|
||||||
|
final http.Response response = await http
|
||||||
|
.get(url, headers: config.defaultHeaders)
|
||||||
|
.timeout(config.timeout);
|
||||||
|
|
||||||
|
if (response.statusCode >= 200 && response.statusCode < 300) {
|
||||||
|
final Object? jsonData = jsonDecode(response.body);
|
||||||
|
if (jsonData is! List) {
|
||||||
|
return ApiError<List<AgentDto>>(ApiErrorInfo(
|
||||||
|
message: 'Expected array response, got ${jsonData.runtimeType}',
|
||||||
|
type: ApiErrorType.serialization,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<AgentDto> agents = jsonData
|
||||||
|
.map((item) => AgentDto.fromJson(item as Map<String, Object?>))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
return ApiSuccess<List<AgentDto>>(agents);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle error responses
|
||||||
|
return ApiError<List<AgentDto>>(ApiErrorInfo(
|
||||||
|
message: 'Failed to load agents',
|
||||||
|
type: ApiErrorType.http,
|
||||||
|
statusCode: response.statusCode,
|
||||||
|
));
|
||||||
|
} on TimeoutException {
|
||||||
|
return ApiError<List<AgentDto>>(ApiErrorInfo(
|
||||||
|
message: 'Request timed out',
|
||||||
|
type: ApiErrorType.timeout,
|
||||||
|
));
|
||||||
|
} on SocketException {
|
||||||
|
return ApiError<List<AgentDto>>(ApiErrorInfo(
|
||||||
|
message: 'No internet connection',
|
||||||
|
type: ApiErrorType.network,
|
||||||
|
));
|
||||||
|
} catch (e) {
|
||||||
|
return ApiError<List<AgentDto>>(ApiErrorInfo(
|
||||||
|
message: 'Unexpected error: $e',
|
||||||
|
type: ApiErrorType.unknown,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:iconsax/iconsax.dart';
|
import 'package:iconsax/iconsax.dart';
|
||||||
import 'package:animate_do/animate_do.dart';
|
import 'package:animate_do/animate_do.dart';
|
||||||
import 'package:getwidget/getwidget.dart';
|
|
||||||
import '../api/api.dart';
|
import '../api/api.dart';
|
||||||
import '../dialogs/create_agent_dialog.dart';
|
import '../dialogs/create_agent_dialog.dart';
|
||||||
|
|
||||||
@ -43,14 +42,26 @@ class _AgentsPageState extends State<AgentsPage> {
|
|||||||
_errorMessage = null;
|
_errorMessage = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: Implement list agents endpoint when available
|
final Result<List<AgentDto>> result = await _apiClient.listAgents();
|
||||||
// For now, show empty state
|
|
||||||
await Future<void>.delayed(const Duration(milliseconds: 500));
|
|
||||||
|
|
||||||
setState(() {
|
result.when(
|
||||||
_agents = [];
|
success: (List<AgentDto> agents) {
|
||||||
_isLoading = false;
|
if (mounted) {
|
||||||
});
|
setState(() {
|
||||||
|
_agents = agents;
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: (ApiErrorInfo error) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_errorMessage = error.message;
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _createAgent(CreateAgentCommand command) async {
|
Future<void> _createAgent(CreateAgentCommand command) async {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user