Skip to content

Commit 944f7f9

Browse files
author
MLeo
committed
add MiMo
1 parent 50cb3b0 commit 944f7f9

File tree

6 files changed

+169
-8
lines changed

6 files changed

+169
-8
lines changed

Sources/Enums/AIProviderEnum.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
enum AIProviderEnum: String, Codable, CaseIterable, Identifiable {
1111
case grok = "Grok"
1212
case deepSeek = "DeepSeek"
13+
case mimo = "MiMo"
1314
case openAI = "OpenAI"
1415
case gemini = "Gemini"
1516
case zenMux = "ZenMux"
@@ -22,6 +23,7 @@ enum AIProviderEnum: String, Codable, CaseIterable, Identifiable {
2223

2324
private static let data:[Self:AIProviderEnumModel] = [
2425
.grok:AIProviderEnumModel.getGrok(),
26+
.mimo:AIProviderEnumModel.getMimo(),
2527
.openAI:AIProviderEnumModel.getOpenAI(),
2628
.gemini:AIProviderEnumModel.getGemini(),
2729
.zenMux:AIProviderEnumModel.getZenMux(),

Sources/Enums/Models/AIProviderEnumModel.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,20 @@ class AIProviderEnumModel {
2929
self.service = service
3030
}
3131

32+
static func getMimo() -> AIProviderEnumModel {
33+
let title = "MiMo(Xiaomi)"
34+
let icon = ""
35+
let supportUrl = "https://platform.xiaomimimo.com/#/console/api-keys"
36+
let APIURL = "https://api.xiaomimimo.com"
37+
let service = MiMoService()
38+
return AIProviderEnumModel(
39+
title: title,
40+
icon: icon,
41+
supportUrl: supportUrl,
42+
APIURL: APIURL,
43+
service: service
44+
)
45+
}
3246
static func getGrok() -> AIProviderEnumModel {
3347
let title = "XAI(Grok)"
3448
let icon = ""
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
//
2+
// GrokService.swift
3+
// iChat
4+
//
5+
// Created by Lion on 2025/4/28.
6+
//
7+
8+
import Foundation
9+
10+
/// MiMo 客户端实现
11+
class MiMoService: AIProtocol {
12+
private let chatPath = "/v1/chat/completions"
13+
private let modelPath = "/v1/models"
14+
private let session: URLSession = .shared
15+
16+
func getModels(provider: AIProvider) async throws -> [Model] {
17+
guard let APIURL = URL(string: provider.APIURL + modelPath) else {
18+
throw AIError.WrongAPIURL
19+
}
20+
// 构建请求
21+
var request = URLRequest(url: APIURL)
22+
request.httpMethod = "GET"
23+
request.setValue(
24+
"Bearer \(provider.APIKey)",
25+
forHTTPHeaderField: "Authorization"
26+
)
27+
request.setValue(
28+
"application/json",
29+
forHTTPHeaderField: "Content-Type"
30+
)
31+
let (data, response) = try await URLSession.shared.data(for: request)
32+
try validateResponse(response)
33+
let modelResponse = try JSONDecoder().decode(
34+
ModelResponse.self,
35+
from: data
36+
)
37+
return modelResponse.data
38+
}
39+
40+
41+
func streamChatResponse(
42+
provider:AIProvider,
43+
model: AIModel,
44+
messages: [ChatMessage],
45+
temperature:Double
46+
)async throws -> AsyncThrowingStream<Delta, Error> {
47+
guard let provider = model.provider else {
48+
throw AIError.MissingProvider
49+
}
50+
guard
51+
let APIURL = URL(string: provider.APIURL + chatPath)
52+
else {
53+
throw AIError.WrongAPIURL
54+
}
55+
// 构建请求
56+
var request = URLRequest(url: APIURL)
57+
request.httpMethod = "POST"
58+
request.setValue(
59+
"Bearer \(provider.APIKey)",
60+
forHTTPHeaderField: "Authorization"
61+
)
62+
request.setValue(
63+
"application/json",
64+
forHTTPHeaderField: "Content-Type"
65+
)
66+
67+
// 构建请求体
68+
let requestBody = StreamRequestBody(
69+
model: model.name,
70+
messages: messages.map({ message in
71+
message.apiRepresentation
72+
}),
73+
temperature: temperature,
74+
stream: true
75+
)
76+
request.httpBody = try JSONEncoder().encode(requestBody)
77+
// 获取流式响应
78+
let (bytes, response) = try await session.bytes(
79+
for: request
80+
)
81+
try validateResponse(response)
82+
return AsyncThrowingStream { continuation in
83+
Task {
84+
do {
85+
// 处理 SSE 流
86+
try await processStream(
87+
bytes: bytes,
88+
continuation: continuation
89+
)
90+
continuation.finish()
91+
} catch {
92+
continuation.finish(throwing: error)
93+
}
94+
}
95+
}
96+
}
97+
98+
99+
100+
// 处理 SSE 流
101+
private func processStream(
102+
bytes: URLSession.AsyncBytes,
103+
continuation: AsyncThrowingStream<Delta, Error>.Continuation
104+
) async throws {
105+
for try await line in bytes.lines {
106+
guard line.hasPrefix("data:") else { continue }
107+
108+
let jsonDataString = line.dropFirst(5).trimmingCharacters(
109+
in: .whitespacesAndNewlines
110+
)
111+
112+
if jsonDataString == "[DONE]" {
113+
return
114+
}
115+
print(jsonDataString)
116+
guard let jsonData = jsonDataString.data(using: .utf8) else {
117+
continue
118+
}
119+
do {
120+
let response = try JSONDecoder().decode(
121+
APIResponseMessage.self,
122+
from: jsonData
123+
)
124+
if let content = response.choices?.first?.delta {
125+
continuation.yield(content)
126+
}
127+
} catch {
128+
print("JSON 解码错误: \(error) for data: \(jsonDataString)")
129+
continue
130+
}
131+
}
132+
}
133+
}

docs/README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22

33
[[中文](https://ai.ichochy.com/README_zh.html)][[English](https://ai.ichochy.com)]
44

5-
iChat is an open-source intelligent chat application that supports DeepSeek, Gemini, Grok, OpenAI, ZenMux, OpenRouter, Cloudflare(AI Gateway) and Custom AI providers. Developed with SwiftUI, it is a native macOS AI client application.
5+
iChat is an open-source intelligent chat application that supports MiMo, DeepSeek, Gemini, Grok, OpenAI, ZenMux, OpenRouter, Cloudflare(AI Gateway) and Custom AI providers. Developed with SwiftUI, it is a native macOS AI client application.
66

77
## Development Environment
88
Development Tool: Xcode 16.3
99
Development Technologies: SwiftUI, SwiftData
1010
System Requirements: macOS 14+
1111

1212
## Features
13-
Currently supports DeepSeek, Gemini, Grok, OpenAI, ZenMux, OpenRouter, Cloudflare(AI Gateway) and Custom AI providers.
13+
Currently supports MiMo, DeepSeek, Gemini, Grok, OpenAI, ZenMux, OpenRouter, Cloudflare(AI Gateway) and Custom AI providers.
1414
1. Allows adding, using, and deleting AI service providers.
1515
2. Enables loading, adding, using, and deleting models.
1616
3. Implements AI conversation functionality with flexible switching between multiple models.
@@ -54,6 +54,10 @@ Currently supports DeepSeek, Gemini, Grok, OpenAI, ZenMux, OpenRouter, Cloudflar
5454
**Please go to "System Settings > Privacy & Security" and click "Open Anyway"** to proceed.
5555

5656
## Updates
57+
### 20251217(0.2(7))
58+
* Add Xiaomi MiMo AI providers
59+
* Overall style detail optimizations
60+
5761
### 20251210(0.1(10))
5862
* Add More AI providers
5963
* Optimize support for macOS 26

docs/README_zh.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[[中文](https://ai.ichochy.com/README_zh.html)][[English](https://ai.ichochy.com)]
44

5-
iChat 是一款开源的智能聊天工具,支持 DeepSeek 、Gemini、Grok、OpenAI、ZenMux、OpenRouter、Cloudflare(AI Gateway)和自定义AI,使用 SwiftUI 开发,macOS 原生 AI 客服端
5+
iChat 是一款开源的智能聊天工具,支持 MiMo、DeepSeek 、Gemini、Grok、OpenAI、ZenMux、OpenRouter、Cloudflare(AI Gateway)和自定义AI,使用 SwiftUI 开发,macOS 原生 AI 客服端
66

77
## 开发环境
88
开发工具:Xcode 16.3
@@ -11,7 +11,7 @@ iChat 是一款开源的智能聊天工具,支持 DeepSeek 、Gemini、Grok、
1111

1212

1313
## 功能说明
14-
现支持 DeepSeek 、Gemini、Grok、OpenAI、ZenMux、OpenRouter、Cloudflare(AI Gateway)和自定义AI
14+
现支持 MiMo、DeepSeek 、Gemini、Grok、OpenAI、ZenMux、OpenRouter、Cloudflare(AI Gateway)和自定义AI
1515
1. 实现了 AI 服务商的添加、使用和删除
1616
2. 实现了模型的加载、添加、使用和删除
1717
3. 实现了 AI 的会话功能,多个模型灵活切换
@@ -56,6 +56,10 @@ iChat 是一款开源的智能聊天工具,支持 DeepSeek 、Gemini、Grok、
5656

5757

5858
## 更新
59+
### 20251217(0.2(7))
60+
* 添加Xiaomi MiMo 提供商
61+
* 整体样式细节优化
62+
5963
### 20251210(0.1(10))
6064
* 添加更多的 AI 提供商
6165
* 优化支持 macOS 26

iChat.xcodeproj/project.pbxproj

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
104ACE262EEEDB0900AA7F2D /* ShareData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 104ACE252EEEDB0900AA7F2D /* ShareData.swift */; };
2727
104ACE272EEEDB0900AA7F2D /* SharePhoto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 104ACE242EEEDB0900AA7F2D /* SharePhoto.swift */; };
2828
104ACE292EEEE60C00AA7F2D /* ShareFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 104ACE282EEEE60C00AA7F2D /* ShareFile.swift */; };
29+
105E1A7B2EF22EA1006C3158 /* MiMoService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 105E1A7A2EF22EA1006C3158 /* MiMoService.swift */; };
2930
107710F22ED89A6E00EB62D4 /* CloudflareService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 107710F12ED89A6E00EB62D4 /* CloudflareService.swift */; };
3031
107C67EE2EDEF96100F2FAD5 /* ZenMuxService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 107C67ED2EDEF96100F2FAD5 /* ZenMuxService.swift */; };
3132
10C9F6AD2E05295A00A8719E /* ResponseContentHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10C9F6AC2E05295A00A8719E /* ResponseContentHelper.swift */; };
@@ -102,6 +103,7 @@
102103
104ACE242EEEDB0900AA7F2D /* SharePhoto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharePhoto.swift; sourceTree = "<group>"; };
103104
104ACE252EEEDB0900AA7F2D /* ShareData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareData.swift; sourceTree = "<group>"; };
104105
104ACE282EEEE60C00AA7F2D /* ShareFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareFile.swift; sourceTree = "<group>"; };
106+
105E1A7A2EF22EA1006C3158 /* MiMoService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MiMoService.swift; sourceTree = "<group>"; };
105107
107710F12ED89A6E00EB62D4 /* CloudflareService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudflareService.swift; sourceTree = "<group>"; };
106108
107C67ED2EDEF96100F2FAD5 /* ZenMuxService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZenMuxService.swift; sourceTree = "<group>"; };
107109
10C9F6AC2E05295A00A8719E /* ResponseContentHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponseContentHelper.swift; sourceTree = "<group>"; };
@@ -352,6 +354,7 @@
352354
isa = PBXGroup;
353355
children = (
354356
B457E88E2DBFA74100598C36 /* GrokService.swift */,
357+
105E1A7A2EF22EA1006C3158 /* MiMoService.swift */,
355358
107C67ED2EDEF96100F2FAD5 /* ZenMuxService.swift */,
356359
1004310D2EE722A60000F96F /* OpenRouterService.swift */,
357360
B440D2742DDF72AC0064B3BE /* GeminiService.swift */,
@@ -547,6 +550,7 @@
547550
B440D2552DD899FF0064B3BE /* SettingsEnum.swift in Sources */,
548551
B497315C2DF1DA49004D9DCD /* MenuBarExtraView.swift in Sources */,
549552
B440D26A2DDDB6E00064B3BE /* ProviderEditorView.swift in Sources */,
553+
105E1A7B2EF22EA1006C3158 /* MiMoService.swift in Sources */,
550554
B497317D2DF8580B004D9DCD /* SessionSideView.swift in Sources */,
551555
107C67EE2EDEF96100F2FAD5 /* ZenMuxService.swift in Sources */,
552556
1004310E2EE722A60000F96F /* OpenRouterService.swift in Sources */,
@@ -719,7 +723,7 @@
719723
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
720724
CODE_SIGN_STYLE = Automatic;
721725
COMBINE_HIDPI_IMAGES = YES;
722-
CURRENT_PROJECT_VERSION = 11;
726+
CURRENT_PROJECT_VERSION = 7;
723727
DEAD_CODE_STRIPPING = YES;
724728
DEVELOPMENT_ASSET_PATHS = "\"Sources/Preview Content\"";
725729
ENABLE_APP_SANDBOX = YES;
@@ -744,7 +748,7 @@
744748
"@executable_path/../Frameworks",
745749
);
746750
MACOSX_DEPLOYMENT_TARGET = 14.0;
747-
MARKETING_VERSION = 0.1;
751+
MARKETING_VERSION = 0.2;
748752
PRODUCT_BUNDLE_IDENTIFIER = com.ichochy.AIChat;
749753
PRODUCT_NAME = "$(TARGET_NAME)";
750754
REGISTER_APP_GROUPS = NO;
@@ -764,7 +768,7 @@
764768
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
765769
CODE_SIGN_STYLE = Automatic;
766770
COMBINE_HIDPI_IMAGES = YES;
767-
CURRENT_PROJECT_VERSION = 11;
771+
CURRENT_PROJECT_VERSION = 7;
768772
DEAD_CODE_STRIPPING = YES;
769773
DEVELOPMENT_ASSET_PATHS = "\"Sources/Preview Content\"";
770774
ENABLE_APP_SANDBOX = YES;
@@ -789,7 +793,7 @@
789793
"@executable_path/../Frameworks",
790794
);
791795
MACOSX_DEPLOYMENT_TARGET = 14.0;
792-
MARKETING_VERSION = 0.1;
796+
MARKETING_VERSION = 0.2;
793797
PRODUCT_BUNDLE_IDENTIFIER = com.ichochy.AIChat;
794798
PRODUCT_NAME = "$(TARGET_NAME)";
795799
REGISTER_APP_GROUPS = NO;

0 commit comments

Comments
 (0)