提交 02c8e358 authored 作者: songchuancai's avatar songchuancai

对接应用聊天接口

上级 94555935
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "chatGPT-chatbox-flutter-main",
"request": "launch",
"type": "dart"
},
{
"name": "chatGPT-chatbox-flutter-main (profile mode)",
"request": "launch",
"type": "dart",
"flutterMode": "profile"
},
{
"name": "chatGPT-chatbox-flutter-main (release mode)",
"request": "launch",
"type": "dart",
"flutterMode": "release"
}
]
}
\ No newline at end of file
......@@ -192,7 +192,7 @@ class _HomePageState extends State<HomePage> {
if (kIsWeb) {
// 设置语速
await flutterTts.setSpeechRate(3);
await flutterTts.setSpeechRate(1);
// 音调
......
import 'package:allen/home_page.dart';
import 'package:allen/pallete.dart';
import 'package:allen/providers.dart';
import 'package:flutter/material.dart';
import 'package:allen/pages/login_page.dart';
import 'package:allen/services/storage_service.dart';
import 'package:allen/models/user.dart';
import 'package:allen/models/app_item.dart';
import 'package:allen/pages/chat_page.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
final prefs = await SharedPreferences.getInstance();
runApp(
ProviderScope(
overrides: [
sharedPreferencesProvider.overrideWithValue(prefs),
],
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
......@@ -33,6 +54,7 @@ class MyApp extends StatelessWidget {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
return snapshot.hasData ? const HomePage() : const LoginPage();
},
),
......@@ -40,21 +62,25 @@ class MyApp extends StatelessWidget {
if (settings.name == '/home') {
return MaterialPageRoute(builder: (context) => const HomePage());
}
if (settings.name == '/login') {
return MaterialPageRoute(builder: (context) => const LoginPage());
}
if (settings.name == '/chat') {
final args = settings.arguments;
if (args is AppItem) {
return MaterialPageRoute(
builder: (context) => ChatPage(app: args),
);
}
return MaterialPageRoute(builder: (context) => const HomePage());
}
return MaterialPageRoute(builder: (context) => const HomePage());
},
);
}
}
\ No newline at end of file
class AppItem {
final String id;
final String name;
final String description;
final String iconUrl;
......@@ -8,6 +9,7 @@ class AppItem {
final bool isDefault;
AppItem({
required this.id,
required this.name,
required this.description,
required this.iconUrl,
......@@ -19,6 +21,7 @@ class AppItem {
factory AppItem.fromJson(Map<String, dynamic> json) {
return AppItem(
id: json['id'] ?? '',
name: json['name'] ?? '',
description: json['description'] ?? '',
iconUrl: json['icon_url'] ?? '',
......
import 'package:flutter/material.dart';
import '../services/api_service.dart';
import '../services/apps_service.dart';
import '../models/app_item.dart';
import '../pallete.dart';
import '../services/storage_service.dart';
import '../models/user.dart';
import 'package:shared_preferences/shared_preferences.dart';
class AppsPage extends StatefulWidget {
const AppsPage({super.key});
......@@ -14,26 +22,36 @@ class AppsPage extends StatefulWidget {
class _AppsPageState extends State<AppsPage> {
late final AppsService _appsService;
List<AppItem> apps = [];
bool isLoading = true;
String? error;
bool _isLoadingMore = false;
int _currentPage = 1;
bool _hasMoreData = true;
final ScrollController _scrollController = ScrollController();
static const int _pageSize = 4;
@override
void initState() {
super.initState();
_initializeService();
_scrollController.addListener(_onScroll);
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
......@@ -62,6 +80,7 @@ class _AppsPageState extends State<AppsPage> {
} else {
setState(() {
apps.addAll(newApps);
_currentPage++;
});
}
......@@ -76,7 +95,9 @@ class _AppsPageState extends State<AppsPage> {
Future<void> _initializeService() async {
final User? user = await StorageService.getUser();
_appsService = AppsService(token: user?.token);
fetchApps();
}
......@@ -86,15 +107,20 @@ class _AppsPageState extends State<AppsPage> {
page: 1,
pageSize: _pageSize,
);
setState(() {
apps = appsList;
isLoading = false;
_currentPage = 1;
_hasMoreData = appsList.length >= _pageSize;
});
} catch (e) {
setState(() {
error = e.toString();
isLoading = false;
});
}
......@@ -186,4 +212,33 @@ class _AppsPageState extends State<AppsPage> {
),
);
}
// Future<void> _onAppTap(AppItem app) async {
// try {
// final apiService = ApiService();
// final storageService =
// StorageService(await SharedPreferences.getInstance());
// final workbenchToken = await apiService.getWorkbenchToken(app.id);
// // 存储token
// await storageService.setWorkbenchToken(workbenchToken);
// if (context.mounted) {
// Navigator.pushNamed(
// context,
// '/chat',
// arguments: app,
// );
// }
// } catch (e) {
// if (context.mounted) {
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(content: Text('进入聊天失败: $e')),
// );
// }
// }
// }
}
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../models/app_item.dart';
import '../services/storage_service.dart';
import '../home_page.dart';
class ChatPage extends StatelessWidget {
import '../services/api_service.dart';
class ChatPage extends ConsumerStatefulWidget {
final AppItem app;
const ChatPage({super.key, required this.app});
@override
ConsumerState<ChatPage> createState() => _ChatPageState();
}
class _ChatPageState extends ConsumerState<ChatPage> {
@override
void initState() {
super.initState();
_initChat();
}
Future<void> _initChat() async {
try {
final apiService = ApiService();
final workbenchToken = await apiService.getWorkbenchToken(widget.app.id);
final prefs = await SharedPreferences.getInstance();
final storageService = StorageService(prefs);
await storageService.saveWorkbenchToken(workbenchToken);
} catch (e) {
debugPrint('初始化聊天失败: $e');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
......@@ -17,14 +53,14 @@ class ChatPage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
app.name,
widget.app.name,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
Text(
app.description,
widget.app.description,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.normal,
......@@ -38,9 +74,9 @@ class ChatPage extends StatelessWidget {
),
),
body: HomePage(
customTitle: app.name,
customDescription: app.description,
customImageUrl: app.iconUrl,
customTitle: widget.app.name,
customDescription: widget.app.description,
customImageUrl: widget.app.iconUrl,
hideNavigation: true,
),
);
......
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'services/api_service.dart';
import 'services/storage_service.dart';
import 'package:shared_preferences/shared_preferences.dart';
final apiServiceProvider = Provider<ApiService>((ref) => ApiService());
final sharedPreferencesProvider = Provider<SharedPreferences>((ref) {
throw UnimplementedError();
});
final storageServiceProvider = Provider<StorageService>((ref) {
final prefs = ref.watch(sharedPreferencesProvider);
return StorageService(prefs);
});
const openAIAPIKey = 'sk-zV0MPjPserDD4gdQ8lE8aUGwxayOwuayogGidos4VO8uxdDL';
const baseUrl = 'https://knowledge-web.apps.iytcloud.com/console/api';
const baseUrl = 'https://knowledge-web.apps.iytcloud.com';
import 'dart:convert';
import 'package:allen/services/storage_service.dart';
import 'package:http/http.dart' as http;
import '../models/user.dart';
import '../secrets.dart';
class ApiService {
static Future<Map<String, dynamic>> login(
String email, String password) async {
final response = await http.post(
Uri.parse('$baseUrl/login/'),
Uri.parse('$baseUrl/console/api/login/'),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
......@@ -19,4 +23,30 @@ class ApiService {
return jsonDecode(response.body);
}
Future<String> getWorkbenchToken(String appId) async {
try {
User? user = await StorageService.getUser();
String token = user?.token ?? '';
final response = await http.post(
Uri.parse('$baseUrl/console/api/apps/workbench'),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer $token',
},
body: jsonEncode({
'app_id': appId,
}),
);
if (response.statusCode == 200) {
return jsonDecode(response.body)['data'];
}
throw Exception('获取会话token失败');
} catch (e) {
throw Exception('获取会话token失败: $e');
}
}
}
......@@ -10,7 +10,7 @@ class AppsService {
Future<List<AppItem>> getApps({int page = 1, int pageSize = 4}) async {
final response = await http.get(
Uri.parse('$baseUrl/apps/?page=$page&pageSize=$pageSize'),
Uri.parse('$baseUrl/console/api/apps/?page=$page&pageSize=$pageSize'),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
......
import 'dart:convert';
import 'package:allen/services/storage_service.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import '../secrets.dart';
class OpenAIService {
final String apiKey = 'sk-OVjS7VE9mT68Uvg7kSFoMnbU6EU836FO';
final String appKey = 'app-FRP2s2wSx01rsE67';
// final String apiKey = 'sk-OVjS7VE9mT68Uvg7kSFoMnbU6EU836FO';
// final String appKey = 'app-FRP2s2wSx01rsE67';
String? conversationId;
Stream<String> chatGPTAPI(String message) async* {
final client = http.Client();
var buffer = StringBuffer();
var prefs = await SharedPreferences.getInstance();
var storageService = StorageService(prefs);
String appKey = storageService.getWorkbenchToken() ?? '';
try {
final request = http.Request('POST', Uri.parse('$baseUrl/openapi/chat'));
final request =
http.Request('POST', Uri.parse('$baseUrl/api/chat-messages'));
request.headers.addAll({
'Content-Type': 'application/json',
'Authorization': 'Bearer $apiKey',
'Authorization': 'Bearer $appKey',
});
Map<String, dynamic> requestBody = {
'app_key': appKey,
'inputs': {},
'query': message,
'stream': true,
'response_mode': 'streaming',
};
if (conversationId != null) {
......
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import '../models/conversation.dart';
import 'package:allen/models/user.dart';
class StorageService {
static const String _conversationsKey = 'conversations';
final SharedPreferences _prefs;
StorageService(this._prefs);
Future<List<Conversation>> getConversations() async {
final String? data = _prefs.getString(_conversationsKey);
if (data == null) return [];
List<dynamic> jsonList = json.decode(data);
return jsonList.map((json) => Conversation.fromJson(json)).toList();
}
......@@ -21,28 +27,36 @@ class StorageService {
final String data = json.encode(
conversations.map((conv) => conv.toJson()).toList(),
);
await _prefs.setString(_conversationsKey, data);
}
Future<void> addConversation(Conversation conversation) async {
final conversations = await getConversations();
conversations.insert(0, conversation);
await saveConversations(conversations);
}
Future<void> updateConversation(Conversation conversation) async {
final conversations = await getConversations();
final index =
conversations.indexWhere((conv) => conv.id == conversation.id);
if (index != -1) {
conversations[index] = conversation;
await saveConversations(conversations);
}
}
Future<void> deleteConversation(String id) async {
final conversations = await getConversations();
conversations.removeWhere((conv) => conv.id == id);
await saveConversations(conversations);
}
......@@ -50,18 +64,37 @@ class StorageService {
static Future<void> saveUser(User user) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_userKey, jsonEncode(user.toJson()));
}
static Future<User?> getUser() async {
final prefs = await SharedPreferences.getInstance();
final userJson = prefs.getString(_userKey);
if (userJson == null) return null;
return User.fromJson(jsonDecode(userJson));
}
static Future<void> clearUser() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove(_userKey);
}
static const String _workbenchTokenKey = 'workbench_token';
Future<void> setWorkbenchToken(String token) async {
await _prefs.setString(_workbenchTokenKey, token);
}
String? getWorkbenchToken() {
return _prefs.getString(_workbenchTokenKey);
}
Future<void> saveWorkbenchToken(String token) async {
await _prefs.setString(_workbenchTokenKey, token);
}
}
......@@ -190,6 +190,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.6.23"
flutter_riverpod:
dependency: "direct main"
description:
name: flutter_riverpod
sha256: "9532ee6db4a943a1ed8383072a2e3eeda041db5657cdf6d2acecf3c21ecbe7e1"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.6.1"
flutter_test:
dependency: "direct dev"
description: flutter
......@@ -392,6 +400,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.8"
riverpod:
dependency: transitive
description:
name: riverpod
sha256: "59062512288d3056b2321804332a13ffdd1bf16df70dcc8e506e411280a72959"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.6.1"
shared_preferences:
dependency: "direct main"
description:
......@@ -501,6 +517,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.11.1"
state_notifier:
dependency: transitive
description:
name: state_notifier
sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.0"
stream_channel:
dependency: transitive
description:
......
name: allen
description: A new Flutter project.
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
......@@ -19,80 +21,278 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
sdk: '>=2.19.5 <3.0.0'
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
flutter:
sdk: flutter
http: ^1.1.0
flutter_riverpod: ^2.5.1
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
speech_to_text: ^6.1.1
flutter_tts: ^3.6.3
animate_do: ^3.0.2
shared_preferences: ^2.0.15
uuid: ^4.3.3
flutter_markdown: ^0.6.18
flutter_highlight: ^0.7.0
markdown: ^7.1.1
audioplayers: ^5.2.1
dev_dependencies:
flutter_test:
sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^2.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
assets:
- assets/images/
- assets/sounds/
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
fonts:
- family: Cera Pro
fonts:
- asset: assets/fonts/Cera-Pro-Bold.otf
- asset: assets/fonts/Cera-Pro-Medium.otf
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论