Skip to content

Commit 4d38b7d

Browse files
authored
Merge pull request #20 from FunctionCalling/feature/aiproxy_deepseek
add deepseek's function calling support
2 parents 2b484c6 + e39c5b4 commit 4d38b7d

File tree

3 files changed

+109
-3
lines changed

3 files changed

+109
-3
lines changed

Package.resolved

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Sources/FunctionCalling-AIProxySwift/FunctionCalling_AIProxySwift.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ extension ToolContainer {
88
public typealias AIProxyOpenAITool = OpenAIChatCompletionRequestBody.Tool
99
public typealias AIProxyAnthropicTool = AnthropicTool
1010
public typealias AIProxyTogetherAITool = TogetherAITool
11+
public typealias AIProxyDeepSeekTool = DeepSeekChatCompletionRequestBody.Tool
1112

1213
// swiftlint:disable:next line_length
1314
// https://github.com/lzell/AIProxySwift?tab=readme-ov-file#how-to-use-openai-structured-outputs-json-schemas-in-a-tool-call
@@ -52,6 +53,20 @@ extension ToolContainer {
5253
)
5354
}
5455
}
56+
57+
// swiftlint:disable:next line_length
58+
// https://github.com/lzell/AIProxySwift/blob/f118afad4941db0ca9c7b6c7f993c483ffd287c6/Sources/AIProxy/DeepSeek/DeepSeekChatCompletionRequestBody.swift#L73-L79
59+
public func toDeepSeekTools() -> [AIProxyDeepSeekTool] {
60+
guard let allTools else { return [] }
61+
62+
return allTools.map { tool in
63+
AIProxyDeepSeekTool.function(
64+
name: tool.name,
65+
description: tool.description,
66+
parameters: tool.inputSchema.toJSONSchema()
67+
)
68+
}
69+
}
5570
}
5671

5772
private extension FunctionCalling.InputSchema {

Tests/FunctionCalling-AIProxySwiftTests/FunctionCallingAIProxySwiftTests.swift

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,97 @@ final class FunctionCallingAIProxySwiftTests: XCTestCase {
295295
}
296296
XCTAssertEqual(enumValues, ["option1", "option2"])
297297
}
298+
299+
func testToDeepSeekTools() throws {
300+
let deepSeekTools = toolContainer.toDeepSeekTools()
301+
302+
XCTAssertEqual(deepSeekTools.count, 1)
303+
304+
let deepSeekTool = try XCTUnwrap(deepSeekTools.first)
305+
306+
guard case .function(let name, let description, let parameters) = deepSeekTool else {
307+
XCTFail("Cannot unwrap function")
308+
return
309+
}
310+
311+
// name
312+
XCTAssertEqual(name, "testTool")
313+
// description
314+
XCTAssertEqual(description, "A test tool")
315+
316+
// inputSchema
317+
guard let parameters else {
318+
XCTFail("Parameters should be a dictionary")
319+
return
320+
}
321+
322+
// inputSchema.type
323+
guard case .string(let type) = parameters["type"] else {
324+
XCTFail("Parameters should be a dictionary")
325+
return
326+
}
327+
XCTAssertEqual(type, "object")
328+
329+
// inputSchema.requiredProperties
330+
guard case .array(let required) = parameters["required"] else {
331+
XCTFail("Parameters should be a dictionary")
332+
return
333+
}
334+
335+
let requiredProperties = required.compactMap { requiredProperty in
336+
switch requiredProperty {
337+
case .string(let propertyName):
338+
return propertyName
339+
default:
340+
return nil
341+
}
342+
}
343+
XCTAssertEqual(requiredProperties, ["testParam"])
344+
345+
// inputSchema.properties
346+
guard case .object(let properties) = parameters["properties"] else {
347+
XCTFail("Parameters should be a dictionary")
348+
return
349+
}
350+
XCTAssertEqual(properties.count, 1)
351+
352+
// inputSchema.properties.testParam
353+
let testParam = try XCTUnwrap(properties["testParam"])
354+
355+
guard case .object(let prop) = testParam else {
356+
XCTFail("Parameters should be a dictionary")
357+
return
358+
}
359+
360+
guard case .string(let type) = prop["type"] else {
361+
XCTFail("Parameters should be a dictionary")
362+
return
363+
}
364+
XCTAssertEqual(type, "string")
365+
366+
guard case .string(let description) = prop["description"] else {
367+
XCTFail("Parameters should be a dictionary")
368+
return
369+
}
370+
XCTAssertEqual(description, "A test parameter")
371+
372+
guard case .array(let enumValueArray) = prop["enum"] else {
373+
XCTFail("Parameters should be a dictionary")
374+
return
375+
}
376+
377+
XCTAssertEqual(enumValueArray.count, 2)
378+
379+
let enumValues = enumValueArray.compactMap { enumValue in
380+
switch enumValue {
381+
case .string(let value):
382+
return value
383+
default:
384+
return nil
385+
}
386+
}
387+
XCTAssertEqual(enumValues, ["option1", "option2"])
388+
}
298389
// swiftlint:enable cyclomatic_complexity
299390
// swiftlint:enable function_body_length
300391
}

0 commit comments

Comments
 (0)