✨ Ai thought basis
This commit is contained in:
66
lib/models/thought.dart
Normal file
66
lib/models/thought.dart
Normal file
@@ -0,0 +1,66 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:island/models/file.dart';
|
||||
|
||||
part 'thought.freezed.dart';
|
||||
part 'thought.g.dart';
|
||||
|
||||
enum ThinkingThoughtRole {
|
||||
assistant(0),
|
||||
user(1);
|
||||
|
||||
const ThinkingThoughtRole(this.value);
|
||||
final int value;
|
||||
|
||||
static ThinkingThoughtRole fromValue(int value) {
|
||||
return values.firstWhere((e) => e.value == value);
|
||||
}
|
||||
}
|
||||
|
||||
class ThinkingThoughtRoleConverter
|
||||
implements JsonConverter<ThinkingThoughtRole, int> {
|
||||
const ThinkingThoughtRoleConverter();
|
||||
|
||||
@override
|
||||
ThinkingThoughtRole fromJson(int json) => ThinkingThoughtRole.fromValue(json);
|
||||
|
||||
@override
|
||||
int toJson(ThinkingThoughtRole object) => object.value;
|
||||
}
|
||||
|
||||
@freezed
|
||||
sealed class StreamThinkingRequest with _$StreamThinkingRequest {
|
||||
const factory StreamThinkingRequest({
|
||||
required String userMessage,
|
||||
String? sequenceId,
|
||||
}) = _StreamThinkingRequest;
|
||||
|
||||
factory StreamThinkingRequest.fromJson(Map<String, dynamic> json) =>
|
||||
_$StreamThinkingRequestFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
sealed class SnThinkingSequence with _$SnThinkingSequence {
|
||||
const factory SnThinkingSequence({
|
||||
required String id,
|
||||
String? topic,
|
||||
required String accountId,
|
||||
}) = _SnThinkingSequence;
|
||||
|
||||
factory SnThinkingSequence.fromJson(Map<String, dynamic> json) =>
|
||||
_$SnThinkingSequenceFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
sealed class SnThinkingThought with _$SnThinkingThought {
|
||||
const factory SnThinkingThought({
|
||||
required String id,
|
||||
String? content,
|
||||
@Default([]) List<SnCloudFile> files,
|
||||
@ThinkingThoughtRoleConverter() required ThinkingThoughtRole role,
|
||||
required String sequenceId,
|
||||
SnThinkingSequence? sequence,
|
||||
}) = _SnThinkingThought;
|
||||
|
||||
factory SnThinkingThought.fromJson(Map<String, dynamic> json) =>
|
||||
_$SnThinkingThoughtFromJson(json);
|
||||
}
|
||||
839
lib/models/thought.freezed.dart
Normal file
839
lib/models/thought.freezed.dart
Normal file
@@ -0,0 +1,839 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'thought.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
/// @nodoc
|
||||
mixin _$StreamThinkingRequest {
|
||||
|
||||
String get userMessage; String? get sequenceId;
|
||||
/// Create a copy of StreamThinkingRequest
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$StreamThinkingRequestCopyWith<StreamThinkingRequest> get copyWith => _$StreamThinkingRequestCopyWithImpl<StreamThinkingRequest>(this as StreamThinkingRequest, _$identity);
|
||||
|
||||
/// Serializes this StreamThinkingRequest to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is StreamThinkingRequest&&(identical(other.userMessage, userMessage) || other.userMessage == userMessage)&&(identical(other.sequenceId, sequenceId) || other.sequenceId == sequenceId));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,userMessage,sequenceId);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'StreamThinkingRequest(userMessage: $userMessage, sequenceId: $sequenceId)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $StreamThinkingRequestCopyWith<$Res> {
|
||||
factory $StreamThinkingRequestCopyWith(StreamThinkingRequest value, $Res Function(StreamThinkingRequest) _then) = _$StreamThinkingRequestCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String userMessage, String? sequenceId
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$StreamThinkingRequestCopyWithImpl<$Res>
|
||||
implements $StreamThinkingRequestCopyWith<$Res> {
|
||||
_$StreamThinkingRequestCopyWithImpl(this._self, this._then);
|
||||
|
||||
final StreamThinkingRequest _self;
|
||||
final $Res Function(StreamThinkingRequest) _then;
|
||||
|
||||
/// Create a copy of StreamThinkingRequest
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? userMessage = null,Object? sequenceId = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
userMessage: null == userMessage ? _self.userMessage : userMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,sequenceId: freezed == sequenceId ? _self.sequenceId : sequenceId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [StreamThinkingRequest].
|
||||
extension StreamThinkingRequestPatterns on StreamThinkingRequest {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _StreamThinkingRequest value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _StreamThinkingRequest() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _StreamThinkingRequest value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _StreamThinkingRequest():
|
||||
return $default(_that);}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _StreamThinkingRequest value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _StreamThinkingRequest() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String userMessage, String? sequenceId)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _StreamThinkingRequest() when $default != null:
|
||||
return $default(_that.userMessage,_that.sequenceId);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String userMessage, String? sequenceId) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _StreamThinkingRequest():
|
||||
return $default(_that.userMessage,_that.sequenceId);}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String userMessage, String? sequenceId)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _StreamThinkingRequest() when $default != null:
|
||||
return $default(_that.userMessage,_that.sequenceId);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
|
||||
class _StreamThinkingRequest implements StreamThinkingRequest {
|
||||
const _StreamThinkingRequest({required this.userMessage, this.sequenceId});
|
||||
factory _StreamThinkingRequest.fromJson(Map<String, dynamic> json) => _$StreamThinkingRequestFromJson(json);
|
||||
|
||||
@override final String userMessage;
|
||||
@override final String? sequenceId;
|
||||
|
||||
/// Create a copy of StreamThinkingRequest
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$StreamThinkingRequestCopyWith<_StreamThinkingRequest> get copyWith => __$StreamThinkingRequestCopyWithImpl<_StreamThinkingRequest>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$StreamThinkingRequestToJson(this, );
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _StreamThinkingRequest&&(identical(other.userMessage, userMessage) || other.userMessage == userMessage)&&(identical(other.sequenceId, sequenceId) || other.sequenceId == sequenceId));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,userMessage,sequenceId);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'StreamThinkingRequest(userMessage: $userMessage, sequenceId: $sequenceId)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$StreamThinkingRequestCopyWith<$Res> implements $StreamThinkingRequestCopyWith<$Res> {
|
||||
factory _$StreamThinkingRequestCopyWith(_StreamThinkingRequest value, $Res Function(_StreamThinkingRequest) _then) = __$StreamThinkingRequestCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String userMessage, String? sequenceId
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$StreamThinkingRequestCopyWithImpl<$Res>
|
||||
implements _$StreamThinkingRequestCopyWith<$Res> {
|
||||
__$StreamThinkingRequestCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _StreamThinkingRequest _self;
|
||||
final $Res Function(_StreamThinkingRequest) _then;
|
||||
|
||||
/// Create a copy of StreamThinkingRequest
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? userMessage = null,Object? sequenceId = freezed,}) {
|
||||
return _then(_StreamThinkingRequest(
|
||||
userMessage: null == userMessage ? _self.userMessage : userMessage // ignore: cast_nullable_to_non_nullable
|
||||
as String,sequenceId: freezed == sequenceId ? _self.sequenceId : sequenceId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// @nodoc
|
||||
mixin _$SnThinkingSequence {
|
||||
|
||||
String get id; String? get topic; String get accountId;
|
||||
/// Create a copy of SnThinkingSequence
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnThinkingSequenceCopyWith<SnThinkingSequence> get copyWith => _$SnThinkingSequenceCopyWithImpl<SnThinkingSequence>(this as SnThinkingSequence, _$identity);
|
||||
|
||||
/// Serializes this SnThinkingSequence to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnThinkingSequence&&(identical(other.id, id) || other.id == id)&&(identical(other.topic, topic) || other.topic == topic)&&(identical(other.accountId, accountId) || other.accountId == accountId));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,topic,accountId);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SnThinkingSequence(id: $id, topic: $topic, accountId: $accountId)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $SnThinkingSequenceCopyWith<$Res> {
|
||||
factory $SnThinkingSequenceCopyWith(SnThinkingSequence value, $Res Function(SnThinkingSequence) _then) = _$SnThinkingSequenceCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String id, String? topic, String accountId
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$SnThinkingSequenceCopyWithImpl<$Res>
|
||||
implements $SnThinkingSequenceCopyWith<$Res> {
|
||||
_$SnThinkingSequenceCopyWithImpl(this._self, this._then);
|
||||
|
||||
final SnThinkingSequence _self;
|
||||
final $Res Function(SnThinkingSequence) _then;
|
||||
|
||||
/// Create a copy of SnThinkingSequence
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? topic = freezed,Object? accountId = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,topic: freezed == topic ? _self.topic : topic // ignore: cast_nullable_to_non_nullable
|
||||
as String?,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [SnThinkingSequence].
|
||||
extension SnThinkingSequencePatterns on SnThinkingSequence {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SnThinkingSequence value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _SnThinkingSequence() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SnThinkingSequence value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _SnThinkingSequence():
|
||||
return $default(_that);}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SnThinkingSequence value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _SnThinkingSequence() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String? topic, String accountId)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _SnThinkingSequence() when $default != null:
|
||||
return $default(_that.id,_that.topic,_that.accountId);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String? topic, String accountId) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _SnThinkingSequence():
|
||||
return $default(_that.id,_that.topic,_that.accountId);}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String? topic, String accountId)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _SnThinkingSequence() when $default != null:
|
||||
return $default(_that.id,_that.topic,_that.accountId);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
|
||||
class _SnThinkingSequence implements SnThinkingSequence {
|
||||
const _SnThinkingSequence({required this.id, this.topic, required this.accountId});
|
||||
factory _SnThinkingSequence.fromJson(Map<String, dynamic> json) => _$SnThinkingSequenceFromJson(json);
|
||||
|
||||
@override final String id;
|
||||
@override final String? topic;
|
||||
@override final String accountId;
|
||||
|
||||
/// Create a copy of SnThinkingSequence
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$SnThinkingSequenceCopyWith<_SnThinkingSequence> get copyWith => __$SnThinkingSequenceCopyWithImpl<_SnThinkingSequence>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$SnThinkingSequenceToJson(this, );
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnThinkingSequence&&(identical(other.id, id) || other.id == id)&&(identical(other.topic, topic) || other.topic == topic)&&(identical(other.accountId, accountId) || other.accountId == accountId));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,topic,accountId);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SnThinkingSequence(id: $id, topic: $topic, accountId: $accountId)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$SnThinkingSequenceCopyWith<$Res> implements $SnThinkingSequenceCopyWith<$Res> {
|
||||
factory _$SnThinkingSequenceCopyWith(_SnThinkingSequence value, $Res Function(_SnThinkingSequence) _then) = __$SnThinkingSequenceCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String id, String? topic, String accountId
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$SnThinkingSequenceCopyWithImpl<$Res>
|
||||
implements _$SnThinkingSequenceCopyWith<$Res> {
|
||||
__$SnThinkingSequenceCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _SnThinkingSequence _self;
|
||||
final $Res Function(_SnThinkingSequence) _then;
|
||||
|
||||
/// Create a copy of SnThinkingSequence
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? topic = freezed,Object? accountId = null,}) {
|
||||
return _then(_SnThinkingSequence(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,topic: freezed == topic ? _self.topic : topic // ignore: cast_nullable_to_non_nullable
|
||||
as String?,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// @nodoc
|
||||
mixin _$SnThinkingThought {
|
||||
|
||||
String get id; String? get content; List<SnCloudFile> get files;@ThinkingThoughtRoleConverter() ThinkingThoughtRole get role; String get sequenceId; SnThinkingSequence? get sequence;
|
||||
/// Create a copy of SnThinkingThought
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnThinkingThoughtCopyWith<SnThinkingThought> get copyWith => _$SnThinkingThoughtCopyWithImpl<SnThinkingThought>(this as SnThinkingThought, _$identity);
|
||||
|
||||
/// Serializes this SnThinkingThought to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnThinkingThought&&(identical(other.id, id) || other.id == id)&&(identical(other.content, content) || other.content == content)&&const DeepCollectionEquality().equals(other.files, files)&&(identical(other.role, role) || other.role == role)&&(identical(other.sequenceId, sequenceId) || other.sequenceId == sequenceId)&&(identical(other.sequence, sequence) || other.sequence == sequence));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,content,const DeepCollectionEquality().hash(files),role,sequenceId,sequence);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SnThinkingThought(id: $id, content: $content, files: $files, role: $role, sequenceId: $sequenceId, sequence: $sequence)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $SnThinkingThoughtCopyWith<$Res> {
|
||||
factory $SnThinkingThoughtCopyWith(SnThinkingThought value, $Res Function(SnThinkingThought) _then) = _$SnThinkingThoughtCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String id, String? content, List<SnCloudFile> files,@ThinkingThoughtRoleConverter() ThinkingThoughtRole role, String sequenceId, SnThinkingSequence? sequence
|
||||
});
|
||||
|
||||
|
||||
$SnThinkingSequenceCopyWith<$Res>? get sequence;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$SnThinkingThoughtCopyWithImpl<$Res>
|
||||
implements $SnThinkingThoughtCopyWith<$Res> {
|
||||
_$SnThinkingThoughtCopyWithImpl(this._self, this._then);
|
||||
|
||||
final SnThinkingThought _self;
|
||||
final $Res Function(SnThinkingThought) _then;
|
||||
|
||||
/// Create a copy of SnThinkingThought
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? content = freezed,Object? files = null,Object? role = null,Object? sequenceId = null,Object? sequence = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,content: freezed == content ? _self.content : content // ignore: cast_nullable_to_non_nullable
|
||||
as String?,files: null == files ? _self.files : files // ignore: cast_nullable_to_non_nullable
|
||||
as List<SnCloudFile>,role: null == role ? _self.role : role // ignore: cast_nullable_to_non_nullable
|
||||
as ThinkingThoughtRole,sequenceId: null == sequenceId ? _self.sequenceId : sequenceId // ignore: cast_nullable_to_non_nullable
|
||||
as String,sequence: freezed == sequence ? _self.sequence : sequence // ignore: cast_nullable_to_non_nullable
|
||||
as SnThinkingSequence?,
|
||||
));
|
||||
}
|
||||
/// Create a copy of SnThinkingThought
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnThinkingSequenceCopyWith<$Res>? get sequence {
|
||||
if (_self.sequence == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $SnThinkingSequenceCopyWith<$Res>(_self.sequence!, (value) {
|
||||
return _then(_self.copyWith(sequence: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Adds pattern-matching-related methods to [SnThinkingThought].
|
||||
extension SnThinkingThoughtPatterns on SnThinkingThought {
|
||||
/// A variant of `map` that fallback to returning `orElse`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SnThinkingThought value)? $default,{required TResult orElse(),}){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _SnThinkingThought() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// Callbacks receives the raw object, upcasted.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case final Subclass2 value:
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SnThinkingThought value) $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _SnThinkingThought():
|
||||
return $default(_that);}
|
||||
}
|
||||
/// A variant of `map` that fallback to returning `null`.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case final Subclass value:
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SnThinkingThought value)? $default,){
|
||||
final _that = this;
|
||||
switch (_that) {
|
||||
case _SnThinkingThought() when $default != null:
|
||||
return $default(_that);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
/// A variant of `when` that fallback to an `orElse` callback.
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return orElse();
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String? content, List<SnCloudFile> files, @ThinkingThoughtRoleConverter() ThinkingThoughtRole role, String sequenceId, SnThinkingSequence? sequence)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _SnThinkingThought() when $default != null:
|
||||
return $default(_that.id,_that.content,_that.files,_that.role,_that.sequenceId,_that.sequence);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
}
|
||||
/// A `switch`-like method, using callbacks.
|
||||
///
|
||||
/// As opposed to `map`, this offers destructuring.
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case Subclass2(:final field2):
|
||||
/// return ...;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String? content, List<SnCloudFile> files, @ThinkingThoughtRoleConverter() ThinkingThoughtRole role, String sequenceId, SnThinkingSequence? sequence) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _SnThinkingThought():
|
||||
return $default(_that.id,_that.content,_that.files,_that.role,_that.sequenceId,_that.sequence);}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
/// It is equivalent to doing:
|
||||
/// ```dart
|
||||
/// switch (sealedClass) {
|
||||
/// case Subclass(:final field):
|
||||
/// return ...;
|
||||
/// case _:
|
||||
/// return null;
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String? content, List<SnCloudFile> files, @ThinkingThoughtRoleConverter() ThinkingThoughtRole role, String sequenceId, SnThinkingSequence? sequence)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _SnThinkingThought() when $default != null:
|
||||
return $default(_that.id,_that.content,_that.files,_that.role,_that.sequenceId,_that.sequence);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
|
||||
class _SnThinkingThought implements SnThinkingThought {
|
||||
const _SnThinkingThought({required this.id, this.content, final List<SnCloudFile> files = const [], @ThinkingThoughtRoleConverter() required this.role, required this.sequenceId, this.sequence}): _files = files;
|
||||
factory _SnThinkingThought.fromJson(Map<String, dynamic> json) => _$SnThinkingThoughtFromJson(json);
|
||||
|
||||
@override final String id;
|
||||
@override final String? content;
|
||||
final List<SnCloudFile> _files;
|
||||
@override@JsonKey() List<SnCloudFile> get files {
|
||||
if (_files is EqualUnmodifiableListView) return _files;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_files);
|
||||
}
|
||||
|
||||
@override@ThinkingThoughtRoleConverter() final ThinkingThoughtRole role;
|
||||
@override final String sequenceId;
|
||||
@override final SnThinkingSequence? sequence;
|
||||
|
||||
/// Create a copy of SnThinkingThought
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$SnThinkingThoughtCopyWith<_SnThinkingThought> get copyWith => __$SnThinkingThoughtCopyWithImpl<_SnThinkingThought>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$SnThinkingThoughtToJson(this, );
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnThinkingThought&&(identical(other.id, id) || other.id == id)&&(identical(other.content, content) || other.content == content)&&const DeepCollectionEquality().equals(other._files, _files)&&(identical(other.role, role) || other.role == role)&&(identical(other.sequenceId, sequenceId) || other.sequenceId == sequenceId)&&(identical(other.sequence, sequence) || other.sequence == sequence));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,content,const DeepCollectionEquality().hash(_files),role,sequenceId,sequence);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SnThinkingThought(id: $id, content: $content, files: $files, role: $role, sequenceId: $sequenceId, sequence: $sequence)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$SnThinkingThoughtCopyWith<$Res> implements $SnThinkingThoughtCopyWith<$Res> {
|
||||
factory _$SnThinkingThoughtCopyWith(_SnThinkingThought value, $Res Function(_SnThinkingThought) _then) = __$SnThinkingThoughtCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String id, String? content, List<SnCloudFile> files,@ThinkingThoughtRoleConverter() ThinkingThoughtRole role, String sequenceId, SnThinkingSequence? sequence
|
||||
});
|
||||
|
||||
|
||||
@override $SnThinkingSequenceCopyWith<$Res>? get sequence;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$SnThinkingThoughtCopyWithImpl<$Res>
|
||||
implements _$SnThinkingThoughtCopyWith<$Res> {
|
||||
__$SnThinkingThoughtCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _SnThinkingThought _self;
|
||||
final $Res Function(_SnThinkingThought) _then;
|
||||
|
||||
/// Create a copy of SnThinkingThought
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? content = freezed,Object? files = null,Object? role = null,Object? sequenceId = null,Object? sequence = freezed,}) {
|
||||
return _then(_SnThinkingThought(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,content: freezed == content ? _self.content : content // ignore: cast_nullable_to_non_nullable
|
||||
as String?,files: null == files ? _self._files : files // ignore: cast_nullable_to_non_nullable
|
||||
as List<SnCloudFile>,role: null == role ? _self.role : role // ignore: cast_nullable_to_non_nullable
|
||||
as ThinkingThoughtRole,sequenceId: null == sequenceId ? _self.sequenceId : sequenceId // ignore: cast_nullable_to_non_nullable
|
||||
as String,sequence: freezed == sequence ? _self.sequence : sequence // ignore: cast_nullable_to_non_nullable
|
||||
as SnThinkingSequence?,
|
||||
));
|
||||
}
|
||||
|
||||
/// Create a copy of SnThinkingThought
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnThinkingSequenceCopyWith<$Res>? get sequence {
|
||||
if (_self.sequence == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $SnThinkingSequenceCopyWith<$Res>(_self.sequence!, (value) {
|
||||
return _then(_self.copyWith(sequence: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// dart format on
|
||||
66
lib/models/thought.g.dart
Normal file
66
lib/models/thought.g.dart
Normal file
@@ -0,0 +1,66 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'thought.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_StreamThinkingRequest _$StreamThinkingRequestFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => _StreamThinkingRequest(
|
||||
userMessage: json['user_message'] as String,
|
||||
sequenceId: json['sequence_id'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$StreamThinkingRequestToJson(
|
||||
_StreamThinkingRequest instance,
|
||||
) => <String, dynamic>{
|
||||
'user_message': instance.userMessage,
|
||||
'sequence_id': instance.sequenceId,
|
||||
};
|
||||
|
||||
_SnThinkingSequence _$SnThinkingSequenceFromJson(Map<String, dynamic> json) =>
|
||||
_SnThinkingSequence(
|
||||
id: json['id'] as String,
|
||||
topic: json['topic'] as String?,
|
||||
accountId: json['account_id'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$SnThinkingSequenceToJson(_SnThinkingSequence instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'topic': instance.topic,
|
||||
'account_id': instance.accountId,
|
||||
};
|
||||
|
||||
_SnThinkingThought _$SnThinkingThoughtFromJson(Map<String, dynamic> json) =>
|
||||
_SnThinkingThought(
|
||||
id: json['id'] as String,
|
||||
content: json['content'] as String?,
|
||||
files:
|
||||
(json['files'] as List<dynamic>?)
|
||||
?.map((e) => SnCloudFile.fromJson(e as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
const [],
|
||||
role: const ThinkingThoughtRoleConverter().fromJson(
|
||||
(json['role'] as num).toInt(),
|
||||
),
|
||||
sequenceId: json['sequence_id'] as String,
|
||||
sequence:
|
||||
json['sequence'] == null
|
||||
? null
|
||||
: SnThinkingSequence.fromJson(
|
||||
json['sequence'] as Map<String, dynamic>,
|
||||
),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$SnThinkingThoughtToJson(_SnThinkingThought instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'content': instance.content,
|
||||
'files': instance.files.map((e) => e.toJson()).toList(),
|
||||
'role': const ThinkingThoughtRoleConverter().toJson(instance.role),
|
||||
'sequence_id': instance.sequenceId,
|
||||
'sequence': instance.sequence?.toJson(),
|
||||
};
|
||||
@@ -37,6 +37,7 @@ import 'package:island/screens/chat/room.dart';
|
||||
import 'package:island/screens/chat/room_detail.dart';
|
||||
import 'package:island/screens/chat/call.dart';
|
||||
import 'package:island/screens/chat/search_messages.dart';
|
||||
import 'package:island/screens/thought/think.dart';
|
||||
import 'package:island/screens/creators/hub.dart';
|
||||
import 'package:island/screens/creators/posts/post_manage_list.dart';
|
||||
import 'package:island/screens/creators/stickers/stickers.dart';
|
||||
@@ -131,6 +132,11 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
return CallScreen(roomId: id);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
name: 'thought',
|
||||
path: '/thought',
|
||||
builder: (context, state) => const ThoughtScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
name: 'logs',
|
||||
path: '/logs',
|
||||
|
||||
@@ -168,6 +168,17 @@ class TabsScreen extends HookConsumerWidget {
|
||||
await PostComposeDialog.show(context);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
),
|
||||
leading: const Icon(Symbols.bubble_chart),
|
||||
title: Text('让我寻思寻思'),
|
||||
onTap: () async {
|
||||
Navigator.of(context).pop();
|
||||
context.pushNamed('thought');
|
||||
},
|
||||
),
|
||||
Consumer(
|
||||
builder: (context, ref, _) {
|
||||
final notificationCount = ref.watch(
|
||||
|
||||
372
lib/screens/thought/think.dart
Normal file
372
lib/screens/thought/think.dart
Normal file
@@ -0,0 +1,372 @@
|
||||
import "dart:async";
|
||||
import "dart:convert";
|
||||
import "package:dio/dio.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_hooks/flutter_hooks.dart";
|
||||
import "package:gap/gap.dart";
|
||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||
import "package:island/models/thought.dart";
|
||||
import "package:island/pods/network.dart";
|
||||
import "package:island/widgets/alert.dart";
|
||||
import "package:island/widgets/app_scaffold.dart";
|
||||
import "package:island/widgets/content/markdown.dart";
|
||||
import "package:island/widgets/response.dart";
|
||||
import "package:material_symbols_icons/material_symbols_icons.dart";
|
||||
import "package:super_sliver_list/super_sliver_list.dart";
|
||||
|
||||
// State management providers
|
||||
final thoughtSequencesProvider = FutureProvider<List<SnThinkingSequence>>((
|
||||
ref,
|
||||
) async {
|
||||
final apiClient = ref.watch(apiClientProvider);
|
||||
final response = await apiClient.get('/insight/thought/sequences');
|
||||
return (response.data as List)
|
||||
.map((e) => SnThinkingSequence.fromJson(e))
|
||||
.toList();
|
||||
});
|
||||
|
||||
final thoughtSequenceProvider =
|
||||
FutureProvider.family<List<SnThinkingThought>, String>((
|
||||
ref,
|
||||
sequenceId,
|
||||
) async {
|
||||
final apiClient = ref.watch(apiClientProvider);
|
||||
final response = await apiClient.get(
|
||||
'/insight/thought/sequences/$sequenceId',
|
||||
);
|
||||
return (response.data as List)
|
||||
.map((e) => SnThinkingThought.fromJson(e))
|
||||
.toList();
|
||||
});
|
||||
|
||||
class ThoughtScreen extends HookConsumerWidget {
|
||||
const ThoughtScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final sequences = ref.watch(thoughtSequencesProvider);
|
||||
final selectedSequenceId = useState<String?>(null);
|
||||
final thoughts =
|
||||
selectedSequenceId.value != null
|
||||
? ref.watch(thoughtSequenceProvider(selectedSequenceId.value!))
|
||||
: const AsyncValue<List<SnThinkingThought>>.data([]);
|
||||
|
||||
final localThoughts = useState<List<SnThinkingThought>>([]);
|
||||
|
||||
final messageController = useTextEditingController();
|
||||
final scrollController = useScrollController();
|
||||
final isStreaming = useState(false);
|
||||
final streamingText = useState<String>('');
|
||||
|
||||
final listController = useMemoized(() => ListController(), []);
|
||||
|
||||
// Update local thoughts when provider data changes
|
||||
useEffect(() {
|
||||
thoughts.whenData((data) => localThoughts.value = data);
|
||||
return null;
|
||||
}, [thoughts]);
|
||||
|
||||
// Scroll to bottom when thoughts change or streaming state changes
|
||||
useEffect(() {
|
||||
if (localThoughts.value.isNotEmpty || isStreaming.value) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
scrollController.animateTo(
|
||||
scrollController.position.maxScrollExtent,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeOut,
|
||||
);
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}, [localThoughts.value.length, isStreaming.value]);
|
||||
|
||||
void sendMessage() async {
|
||||
if (messageController.text.trim().isEmpty) return;
|
||||
|
||||
final userMessage = messageController.text.trim();
|
||||
|
||||
// Add user message to local thoughts
|
||||
final userThought = SnThinkingThought(
|
||||
id: 'temp-user-${DateTime.now().millisecondsSinceEpoch}',
|
||||
content: userMessage,
|
||||
files: [],
|
||||
role: ThinkingThoughtRole.user,
|
||||
sequenceId: selectedSequenceId.value ?? '',
|
||||
sequence:
|
||||
selectedSequenceId.value != null
|
||||
? thoughts.value?.firstOrNull?.sequence ??
|
||||
SnThinkingSequence(
|
||||
id: selectedSequenceId.value!,
|
||||
accountId: '',
|
||||
)
|
||||
: SnThinkingSequence(id: '', accountId: ''),
|
||||
);
|
||||
localThoughts.value = [userThought, ...localThoughts.value];
|
||||
|
||||
final request = StreamThinkingRequest(
|
||||
userMessage: userMessage,
|
||||
sequenceId: selectedSequenceId.value,
|
||||
);
|
||||
|
||||
try {
|
||||
isStreaming.value = true;
|
||||
streamingText.value = '';
|
||||
|
||||
final apiClient = ref.read(apiClientProvider);
|
||||
final response = await apiClient.post(
|
||||
'/insight/thought',
|
||||
data: request.toJson(),
|
||||
options: Options(
|
||||
responseType: ResponseType.stream,
|
||||
sendTimeout: Duration(minutes: 1),
|
||||
receiveTimeout: Duration(minutes: 1),
|
||||
),
|
||||
);
|
||||
|
||||
final stream = response.data.stream;
|
||||
final completer = Completer<String>();
|
||||
final buffer = StringBuffer();
|
||||
|
||||
stream.listen(
|
||||
(data) {
|
||||
final chunk = utf8.decode(data);
|
||||
buffer.write(chunk);
|
||||
streamingText.value = buffer.toString();
|
||||
},
|
||||
onDone: () {
|
||||
completer.complete(buffer.toString());
|
||||
isStreaming.value = false;
|
||||
// Parse the response and add AI thought
|
||||
try {
|
||||
final lines = buffer.toString().split('\n');
|
||||
final lastLine = lines.lastWhere(
|
||||
(line) => line.trim().isNotEmpty,
|
||||
);
|
||||
final responseJson = jsonDecode(lastLine);
|
||||
final aiThought = SnThinkingThought.fromJson(responseJson);
|
||||
localThoughts.value = [aiThought, ...localThoughts.value];
|
||||
} catch (e) {
|
||||
showErrorAlert('Failed to parse AI response');
|
||||
}
|
||||
},
|
||||
onError: (error) {
|
||||
completer.completeError(error);
|
||||
isStreaming.value = false;
|
||||
// Handle streaming response errors differently
|
||||
if (error is DioException && error.response?.data is ResponseBody) {
|
||||
// For streaming responses, show a generic error message
|
||||
showErrorAlert('Failed to get AI response. Please try again.');
|
||||
} else {
|
||||
showErrorAlert(error);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
messageController.clear();
|
||||
} catch (error) {
|
||||
isStreaming.value = false;
|
||||
showErrorAlert(error);
|
||||
}
|
||||
}
|
||||
|
||||
Widget thoughtItem(SnThinkingThought thought) => Container(
|
||||
margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
thought.role == ThinkingThoughtRole.assistant
|
||||
? Theme.of(context).colorScheme.surfaceContainerHighest
|
||||
: Theme.of(context).colorScheme.primaryContainer,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
thought.role == ThinkingThoughtRole.assistant
|
||||
? Symbols.smart_toy
|
||||
: Symbols.person,
|
||||
size: 20,
|
||||
),
|
||||
const Gap(8),
|
||||
Text(
|
||||
thought.role == ThinkingThoughtRole.assistant
|
||||
? 'AI Assistant'
|
||||
: 'You',
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
),
|
||||
],
|
||||
),
|
||||
const Gap(8),
|
||||
if (thought.content != null)
|
||||
MarkdownTextContent(
|
||||
content: thought.content!,
|
||||
textStyle: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Widget streamingThoughtItem() => Container(
|
||||
margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(Symbols.smart_toy, size: 20),
|
||||
const Gap(8),
|
||||
Text(
|
||||
'AI Assistant',
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
),
|
||||
const Spacer(),
|
||||
SizedBox(
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Gap(8),
|
||||
MarkdownTextContent(
|
||||
content: streamingText.value,
|
||||
textStyle: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
return AppScaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('AI Thought'),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Symbols.history),
|
||||
onPressed: () {
|
||||
// Show sequence selector
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder:
|
||||
(context) => sequences.when(
|
||||
data:
|
||||
(seqs) => ListView.builder(
|
||||
itemCount: seqs.length,
|
||||
itemBuilder: (context, index) {
|
||||
final seq = seqs[index];
|
||||
return ListTile(
|
||||
title: Text(
|
||||
seq.topic ?? 'Untitled Conversation',
|
||||
),
|
||||
onTap: () {
|
||||
selectedSequenceId.value = seq.id;
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
loading:
|
||||
() =>
|
||||
const Center(child: CircularProgressIndicator()),
|
||||
error:
|
||||
(error, _) => ResponseErrorWidget(
|
||||
error: error,
|
||||
onRetry:
|
||||
() => ref.invalidate(thoughtSequencesProvider),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: thoughts.when(
|
||||
data:
|
||||
(thoughtList) => SuperListView.builder(
|
||||
listController: listController,
|
||||
controller: scrollController,
|
||||
padding: const EdgeInsets.only(bottom: 16),
|
||||
reverse: true,
|
||||
itemCount:
|
||||
localThoughts.value.length +
|
||||
(isStreaming.value ? 1 : 0),
|
||||
itemBuilder: (context, index) {
|
||||
if (isStreaming.value && index == 0) {
|
||||
return streamingThoughtItem();
|
||||
}
|
||||
final thoughtIndex =
|
||||
isStreaming.value ? index - 1 : index;
|
||||
final thought = localThoughts.value[thoughtIndex];
|
||||
return thoughtItem(thought);
|
||||
},
|
||||
),
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error:
|
||||
(error, _) => ResponseErrorWidget(
|
||||
error: error,
|
||||
onRetry:
|
||||
() =>
|
||||
selectedSequenceId.value != null
|
||||
? ref.invalidate(
|
||||
thoughtSequenceProvider(
|
||||
selectedSequenceId.value!,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
border: Border(
|
||||
top: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: messageController,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Ask me anything...',
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 12,
|
||||
),
|
||||
),
|
||||
maxLines: null,
|
||||
textInputAction: TextInputAction.send,
|
||||
onSubmitted: (_) => sendMessage(),
|
||||
),
|
||||
),
|
||||
const Gap(8),
|
||||
IconButton.filled(
|
||||
onPressed: isStreaming.value ? null : sendMessage,
|
||||
icon: Icon(isStreaming.value ? Symbols.stop : Symbols.send),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user