Skip to content

Commit

Permalink
Create thread run repository and usecase (#89)
Browse files Browse the repository at this point in the history
  • Loading branch information
banghuazhao authored Jan 27, 2025
1 parent 8622d85 commit 30a1a1d
Show file tree
Hide file tree
Showing 16 changed files with 443 additions and 56 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

Expand Down
3 changes: 1 addition & 2 deletions lib/presentation/chat/views/composites_tools.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import '../../settings/views/apply_expert_page.dart';
import '../../settings/views/login_page.dart';
import '../../settings/views/user_profile_page.dart';
import '../viewModels/chat_view_model.dart';
import 'aitool_creation.dart';
import 'chat_screen.dart';
import 'composites_tool_creation.dart';

class CompositesTools extends StatefulWidget {
@override
Expand Down
57 changes: 57 additions & 0 deletions packages/data/lib/repositories/assistants_repository_impl.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import 'dart:convert';
import 'dart:io';

import 'package:domain/entities/assistant.dart';
import 'package:http/http.dart' as http;

import 'package:domain/repositories_abstract/assistants_repository.dart';

import '../utils/api_constants.dart';

class AssistantsRepositoryImp implements AssistantsRepository {
final http.Client client;

AssistantsRepositoryImp(
{required this.client});

@override
Future<Assistant> createCompositeAssistant() async {
final request =
http.Request('POST', Uri.parse(ApiConstants.assistantsEndpoint))
..headers.addAll({
'Content-Type': 'application/json',
'Authorization': 'Bearer ${ApiConstants.apiKey}',
'OpenAI-Beta': 'assistants=v2',
})
..body = jsonEncode({
"model": "gpt-4o",
"name": "Composites AI",
'description':
"You are an expert in composite materials and structures. Please answer questions related to composites simulation, design and manufacturing."
});

// 2. Send the request
final http.StreamedResponse streamedResponse = await client.send(request);
final String responseBody = await streamedResponse.stream.bytesToString();

// 3. Handle success or throw an error
if (streamedResponse.statusCode == 200 ||
streamedResponse.statusCode == 201) {
final Map<String, dynamic> jsonResponse = jsonDecode(responseBody);
return Assistant.fromJson(jsonResponse);
} else {
// Provide as much detail as possible for debugging
throw HttpException(
'Failed to create assistant. '
'Status: ${streamedResponse.statusCode}, '
'Response: $responseBody',
uri: request.url,
);
}
}

@override
String getCompositeAssistantId() {
return "asst_pxUDI3A9Q8afCqT9cqgUkWQP";
}
}
82 changes: 75 additions & 7 deletions packages/data/lib/repositories/messages_repository_impl.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import 'dart:convert';
import 'dart:io';

import 'package:domain/entities/assistant_message.dart';
import 'package:domain/entities/message.dart';
import 'package:domain/entities/thread.dart';
import 'package:domain/entities/thread_message.dart';
import 'package:domain/repositories_abstract/messages_repository.dart';
import 'package:http/http.dart' as http;

Expand All @@ -15,17 +13,17 @@ class MessagesRepositoryImp implements MessagesRepository {
MessagesRepositoryImp({required this.client});

@override
Future<AssistantMessage> createMessage(Thread thread, Message message) async {
Future<ThreadMessage> createMessage(String threadId, String message) async {
final request = http.Request('POST',
Uri.parse(ApiConstants.threadsEndpoint + "/${thread.id}/messages"))
Uri.parse("${ApiConstants.threadsEndpoint}/$threadId/messages"))
..headers.addAll({
'Content-Type': 'application/json',
'Authorization': 'Bearer ${ApiConstants.apiKey}',
'OpenAI-Beta': 'assistants=v2',
})
..body = jsonEncode({
"role": "user",
"content": message.content,
"content": message,
});

// 2. Send the request
Expand All @@ -36,7 +34,7 @@ class MessagesRepositoryImp implements MessagesRepository {
if (streamedResponse.statusCode == 200 ||
streamedResponse.statusCode == 201) {
final Map<String, dynamic> jsonResponse = jsonDecode(responseBody);
return AssistantMessage.fromJson(jsonResponse);
return ThreadMessage.fromJson(jsonResponse);
} else {
// Provide as much detail as possible for debugging
throw HttpException(
Expand All @@ -47,4 +45,74 @@ class MessagesRepositoryImp implements MessagesRepository {
);
}
}

@override
Future<List<ThreadMessage>> listMessage(String threadId) async {
final request = http.Request('GET',
Uri.parse("${ApiConstants.threadsEndpoint}/$threadId/messages"))
..headers.addAll({
'Content-Type': 'application/json',
'Authorization': 'Bearer ${ApiConstants.apiKey}',
'OpenAI-Beta': 'assistants=v2',
});

// 2. Send the request
final http.StreamedResponse streamedResponse = await client.send(request);
final String responseBody = await streamedResponse.stream.bytesToString();

// 3. Handle success or throw an error
if (streamedResponse.statusCode == 200 ||
streamedResponse.statusCode == 201) {
final Map<String, dynamic> jsonResponse = jsonDecode(responseBody);

final List<dynamic> data = jsonResponse['data'];

// Map each JSON object to an AssistantMessage
final List<ThreadMessage> messages = data
.map((jsonItem) =>
ThreadMessage.fromJson(jsonItem as Map<String, dynamic>))
.toList();

return messages;
} else {
// Provide as much detail as possible for debugging
throw HttpException(
'Failed to list messages.'
'Status: ${streamedResponse.statusCode}, '
'Response: $responseBody',
uri: request.url,
);
}
}

@override
Future<ThreadMessage> retrieveMessage(String threadId, String messageId) async {
final request = http.Request('GET',
Uri.parse("${ApiConstants.threadsEndpoint}/$threadId/messages/$messageId"))
..headers.addAll({
'Content-Type': 'application/json',
'Authorization': 'Bearer ${ApiConstants.apiKey}',
'OpenAI-Beta': 'assistants=v2',
});

// 2. Send the request
final http.StreamedResponse streamedResponse = await client.send(request);
final String responseBody = await streamedResponse.stream.bytesToString();

// 3. Handle success or throw an error
if (streamedResponse.statusCode == 200 ||
streamedResponse.statusCode == 201) {
final Map<String, dynamic> jsonResponse = jsonDecode(responseBody);

return ThreadMessage.fromJson(jsonResponse);
} else {
// Provide as much detail as possible for debugging
throw HttpException(
'Failed to list messages.'
'Status: ${streamedResponse.statusCode}, '
'Response: $responseBody',
uri: request.url,
);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,30 @@
import 'dart:convert';
import 'dart:io';

import 'package:domain/entities/assistant.dart';
import 'package:domain/entities/thread.dart';
import 'package:domain/entities/thread_message.dart';
import 'package:domain/entities/thread_run.dart';
import 'package:domain/repositories_abstract/thread_runs_repository.dart';
import 'package:http/http.dart' as http;

import 'package:domain/repositories_abstract/assistant_repository.dart';

import '../utils/api_constants.dart';

class AssistantRepositoryImp implements AssistantRepository {
class ThreadRunsRepositoryImp implements ThreadRunsRepository {
final http.Client client;

AssistantRepositoryImp(
{required this.client});
ThreadRunsRepositoryImp({required this.client});

@override
Future<Assistant> createCompositeAssistant() async {
Future<ThreadRun> createRun(Assistant assistant) async {
final request =
http.Request('POST', Uri.parse(ApiConstants.assistantsEndpoint))
http.Request('POST', Uri.parse("${ApiConstants.threadsEndpoint}/runs"))
..headers.addAll({
'Content-Type': 'application/json',
'Authorization': 'Bearer ${ApiConstants.apiKey}',
'OpenAI-Beta': 'assistants=v2',
})
..body = jsonEncode({
"model": "gpt-4o",
"name": "Composites AI",
'description':
"You are an expert in composite materials and structures. Please answer questions related to composites simulation, design and manufacturing."
"assistant_id": assistant.id,
});

// 2. Send the request
Expand All @@ -39,31 +35,35 @@ class AssistantRepositoryImp implements AssistantRepository {
if (streamedResponse.statusCode == 200 ||
streamedResponse.statusCode == 201) {
final Map<String, dynamic> jsonResponse = jsonDecode(responseBody);
return Assistant.fromJson(jsonResponse);
return ThreadRun.fromJson(jsonResponse);
} else {
// Provide as much detail as possible for debugging
throw HttpException(
'Failed to create assistant. '
'Failed to thread run.'
'Status: ${streamedResponse.statusCode}, '
'Response: $responseBody',
uri: request.url,
);
}
}

@override
String getCompositeAssistantId() {
return "asst_pxUDI3A9Q8afCqT9cqgUkWQP";
}

@override
Future<Thread> createThread() async {
Future<ThreadRun> createMessageAndRun(
Assistant assistant, String message) async {
final request =
http.Request('POST', Uri.parse(ApiConstants.threadsEndpoint))
http.Request('POST', Uri.parse("${ApiConstants.threadsEndpoint}/runs"))
..headers.addAll({
'Content-Type': 'application/json',
'Authorization': 'Bearer ${ApiConstants.apiKey}',
'OpenAI-Beta': 'assistants=v2',
})
..body = jsonEncode({
"assistant_id": assistant.id,
"thread": {
"messages": [
{"role": "user", "content": message}
]
}
});

// 2. Send the request
Expand All @@ -74,11 +74,11 @@ class AssistantRepositoryImp implements AssistantRepository {
if (streamedResponse.statusCode == 200 ||
streamedResponse.statusCode == 201) {
final Map<String, dynamic> jsonResponse = jsonDecode(responseBody);
return Thread.fromJson(jsonResponse);
return ThreadRun.fromJson(jsonResponse);
} else {
// Provide as much detail as possible for debugging
throw HttpException(
'Failed to create a thread. '
'Failed to thread run.'
'Status: ${streamedResponse.statusCode}, '
'Response: $responseBody',
uri: request.url,
Expand Down
31 changes: 31 additions & 0 deletions packages/data/lib/repositories/threads_repository_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,35 @@ class ThreadsRepositoryImp implements ThreadsRepository {
);
}
}

@override
Future<Thread> retrieveThread(String threadId) async {
final request =
http.Request('POST', Uri.parse("${ApiConstants.threadsEndpoint}/$threadId"))
..headers.addAll({
'Content-Type': 'application/json',
'Authorization': 'Bearer ${ApiConstants.apiKey}',
'OpenAI-Beta': 'assistants=v2',
});

// 2. Send the request
final http.StreamedResponse streamedResponse = await client.send(request);
final String responseBody = await streamedResponse.stream.bytesToString();

// 3. Handle success or throw an error
if (streamedResponse.statusCode == 200 ||
streamedResponse.statusCode == 201) {
final Map<String, dynamic> jsonResponse = jsonDecode(responseBody);
return Thread.fromJson(jsonResponse);
} else {
// Provide as much detail as possible for debugging
throw HttpException(
'Failed to retrieve a thread. '
'Status: ${streamedResponse.statusCode}, '
'Response: $responseBody',
uri: request.url,
);
}
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
class AssistantMessage {
class ThreadMessage {
final String id;
final String object;
final int createdAt;
Expand All @@ -10,7 +10,7 @@ class AssistantMessage {
final List<dynamic> attachments;
final Map<String, dynamic> metadata;

AssistantMessage({
ThreadMessage({
required this.id,
required this.object,
required this.createdAt,
Expand All @@ -23,15 +23,15 @@ class AssistantMessage {
required this.metadata,
});

factory AssistantMessage.fromJson(Map<String, dynamic> json) {
factory ThreadMessage.fromJson(Map<String, dynamic> json) {
var contentList = <Content>[];
if (json['content'] != null) {
contentList = (json['content'] as List)
.map((i) => Content.fromJson(i))
.toList();
}

return AssistantMessage(
return ThreadMessage(
id: json['id'],
object: json['object'],
createdAt: json['created_at'],
Expand Down
Loading

0 comments on commit 30a1a1d

Please sign in to comment.