Files
keyboard/keyBoard/Class/AiTalk/VM/AiVM.m

338 lines
11 KiB
Mathematica
Raw Normal View History

2026-01-22 13:47:34 +08:00
//
// AiVM.m
// keyBoard
//
// Created by Mac on 2026/1/22.
//
#import "AiVM.h"
2026-01-22 22:03:56 +08:00
#import "KBAPI.h"
#import "KBNetworkManager.h"
#import <MJExtension/MJExtension.h>
@implementation KBAiSyncData
- (void)setAudioBase64:(NSString *)audioBase64 {
if (![audioBase64 isKindOfClass:[NSString class]]) {
_audioBase64 = nil;
self.audioData = nil;
return;
}
_audioBase64 = [audioBase64 copy];
if (_audioBase64.length == 0) {
self.audioData = nil;
return;
}
NSString *cleanBase64 = _audioBase64;
NSRange commaRange = [cleanBase64 rangeOfString:@","];
if ([cleanBase64 hasPrefix:@"data:"] && commaRange.location != NSNotFound) {
cleanBase64 = [cleanBase64 substringFromIndex:commaRange.location + 1];
}
self.audioData = [[NSData alloc]
initWithBase64EncodedString:cleanBase64
options:NSDataBase64DecodingIgnoreUnknownCharacters];
}
@end
@implementation KBAiSyncResponse
@end
@implementation KBAiMessageData
@end
@implementation KBAiMessageResponse
@end
2026-01-22 13:47:34 +08:00
@implementation AiVM
2026-01-22 22:03:56 +08:00
- (void)syncChatWithTranscript:(NSString *)transcript
completion:(AiVMSyncCompletion)completion {
if (transcript.length == 0) {
NSError *error = [NSError
errorWithDomain:@"AiVM"
code:-1
userInfo:@{NSLocalizedDescriptionKey : @"transcript is empty"}];
if (completion) {
completion(nil, error);
}
return;
}
NSDictionary *params = @{ @"transcript" : transcript ?: @"" };
CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
NSLog(@"[AiVM] /chat/sync request: %@", params);
[[KBNetworkManager shared]
POST:API_AI_CHAT_SYNC
jsonBody:params
headers:nil
autoShowBusinessError:NO
completion:^(NSDictionary *_Nullable json,
NSURLResponse *_Nullable response,
NSError *_Nullable error) {
CFAbsoluteTime elapsed =
(CFAbsoluteTimeGetCurrent() - startTime) * 1000.0;
if (error) {
NSLog(@"[AiVM] /chat/sync failed: %@",
error.localizedDescription ?: @"");
NSLog(@"[AiVM] /chat/sync duration: %.0f ms", elapsed);
if (completion) {
completion(nil, error);
}
return;
}
NSLog(@"[AiVM] /chat/sync response received");
NSLog(@"[AiVM] /chat/sync duration: %.0f ms", elapsed);
KBAiSyncResponse *model =
[KBAiSyncResponse mj_objectWithKeyValues:json];
if (completion) {
completion(model, nil);
}
}];
}
- (void)requestChatMessageWithContent:(NSString *)content
completion:(AiVMMessageCompletion)completion {
if (content.length == 0) {
NSError *error = [NSError
errorWithDomain:@"AiVM"
code:-1
userInfo:@{NSLocalizedDescriptionKey : @"content is empty"}];
if (completion) {
completion(nil, error);
}
return;
}
NSString *encodedContent =
[content stringByAddingPercentEncodingWithAllowedCharacters:
[NSCharacterSet URLQueryAllowedCharacterSet]];
NSString *path = [NSString
stringWithFormat:@"%@?content=%@", API_AI_CHAT_MESSAGE,
encodedContent ?: @""];
NSDictionary *params = @{ @"content" : content ?: @"" };
[[KBNetworkManager shared]
POST:path
jsonBody:params
headers:nil
autoShowBusinessError:NO
completion:^(NSDictionary *_Nullable json,
NSURLResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
if (completion) {
completion(nil, error);
}
return;
}
KBAiMessageResponse *model =
[KBAiMessageResponse mj_objectWithKeyValues:json];
2026-01-23 21:51:37 +08:00
id dataObj = json[@"data"];
if (!model.data && [dataObj isKindOfClass:[NSString class]]) {
KBAiMessageData *data = [[KBAiMessageData alloc] init];
data.content = (NSString *)dataObj;
model.data = data;
}
2026-01-22 22:03:56 +08:00
if (completion) {
completion(model, nil);
}
}];
}
2026-01-23 21:51:37 +08:00
- (void)requestAudioWithAudioId:(NSString *)audioId
completion:(AiVMAudioURLCompletion)completion {
if (audioId.length == 0) {
2026-01-22 22:03:56 +08:00
NSError *error = [NSError
errorWithDomain:@"AiVM"
code:-1
2026-01-23 21:51:37 +08:00
userInfo:@{NSLocalizedDescriptionKey : @"audioId is empty"}];
2026-01-22 22:03:56 +08:00
if (completion) {
completion(nil, error);
}
return;
}
2026-01-23 21:51:37 +08:00
NSString *path = [NSString stringWithFormat:@"/chat/audio/%@", audioId];
[[KBNetworkManager shared]
GET:path
parameters:nil
headers:nil
autoShowBusinessError:NO
completion:^(NSDictionary *_Nullable json,
NSURLResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
if (completion) {
completion(nil, error);
}
return;
}
// URL
NSString *audioURL = nil;
if ([json isKindOfClass:[NSDictionary class]]) {
// {"code": 0, "data": {"audioUrl": "http://...", "url": "http://..."}}
id dataObj = json[@"data"];
if ([dataObj isKindOfClass:[NSDictionary class]]) {
NSDictionary *dataDict = (NSDictionary *)dataObj;
// 使 audioUrl url
id audioUrlObj = dataDict[@"audioUrl"] ?: dataDict[@"url"];
// NSNull
if (audioUrlObj && ![audioUrlObj isKindOfClass:[NSNull class]] && [audioUrlObj isKindOfClass:[NSString class]]) {
audioURL = (NSString *)audioUrlObj;
}
} else if ([dataObj isKindOfClass:[NSString class]]) {
audioURL = (NSString *)dataObj;
}
// URL
if (!audioURL) {
id audioUrlObj = json[@"audioUrl"] ?: json[@"url"];
if (audioUrlObj && ![audioUrlObj isKindOfClass:[NSNull class]] && [audioUrlObj isKindOfClass:[NSString class]]) {
audioURL = (NSString *)audioUrlObj;
}
}
}
// audioURL nil nil
if (!audioURL || audioURL.length == 0) {
if (completion) {
completion(nil, nil); // nil
}
return;
}
if (completion) {
completion(audioURL, nil);
}
}];
2026-01-22 22:03:56 +08:00
}
2026-01-26 16:53:41 +08:00
#pragma mark -
- (void)fetchPersonasWithPageNum:(NSInteger)pageNum
pageSize:(NSInteger)pageSize
completion:(void (^)(KBPersonaPageModel * _Nullable, NSError * _Nullable))completion {
NSDictionary *params = @{
@"pageNum": @(pageNum),
@"pageSize": @(pageSize)
};
NSLog(@"[AiVM] /ai-companion/page request: %@", params);
[[KBNetworkManager shared]
POST:@"/ai-companion/page"
jsonBody:params
headers:nil
autoShowBusinessError:NO
completion:^(NSDictionary *_Nullable json,
NSURLResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
NSLog(@"[AiVM] /ai-companion/page failed: %@", error.localizedDescription ?: @"");
if (completion) {
completion(nil, error);
}
return;
}
NSLog(@"[AiVM] /ai-companion/page response: %@", json);
//
NSInteger code = [json[@"code"] integerValue];
if (code != 0) {
NSString *message = json[@"message"] ?: @"请求失败";
NSError *bizError = [NSError errorWithDomain:@"AiVM"
code:code
userInfo:@{NSLocalizedDescriptionKey: message}];
if (completion) {
completion(nil, bizError);
}
return;
}
//
id dataObj = json[@"data"];
if ([dataObj isKindOfClass:[NSDictionary class]]) {
KBPersonaPageModel *pageModel = [KBPersonaPageModel mj_objectWithKeyValues:dataObj];
if (completion) {
completion(pageModel, nil);
}
} else {
NSError *parseError = [NSError errorWithDomain:@"AiVM"
code:-1
userInfo:@{NSLocalizedDescriptionKey: @"数据格式错误"}];
if (completion) {
completion(nil, parseError);
}
}
}];
}
2026-01-26 18:17:02 +08:00
#pragma mark -
- (void)fetchChatHistoryWithCompanionId:(NSInteger)companionId
pageNum:(NSInteger)pageNum
pageSize:(NSInteger)pageSize
completion:(void (^)(KBChatHistoryPageModel * _Nullable, NSError * _Nullable))completion {
NSDictionary *params = @{
@"companionId": @(companionId),
@"pageNum": @(pageNum),
@"pageSize": @(pageSize)
};
NSLog(@"[AiVM] /chat/history request: %@", params);
[[KBNetworkManager shared]
POST:@"/chat/history"
jsonBody:params
headers:nil
autoShowBusinessError:NO
completion:^(NSDictionary *_Nullable json,
NSURLResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
NSLog(@"[AiVM] /chat/history failed: %@", error.localizedDescription ?: @"");
if (completion) {
completion(nil, error);
}
return;
}
NSLog(@"[AiVM] /chat/history response: %@", json);
//
NSInteger code = [json[@"code"] integerValue];
if (code != 0) {
NSString *message = json[@"message"] ?: @"请求失败";
NSError *bizError = [NSError errorWithDomain:@"AiVM"
code:code
userInfo:@{NSLocalizedDescriptionKey: message}];
if (completion) {
completion(nil, bizError);
}
return;
}
//
id dataObj = json[@"data"];
if ([dataObj isKindOfClass:[NSDictionary class]]) {
KBChatHistoryPageModel *pageModel = [KBChatHistoryPageModel mj_objectWithKeyValues:dataObj];
if (completion) {
completion(pageModel, nil);
}
} else {
NSError *parseError = [NSError errorWithDomain:@"AiVM"
code:-1
userInfo:@{NSLocalizedDescriptionKey: @"数据格式错误"}];
if (completion) {
completion(nil, parseError);
}
}
}];
}
2026-01-22 13:47:34 +08:00
@end