Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[env]
MACOSX_DEPLOYMENT_TARGET = { value = "14.2", force = true }
TOOLCHAINS = { value = "com.apple.dt.toolchain.XcodeDefault", force = true }

[target.'cfg(all(windows, target_env = "msvc"))']
rustflags = [
Expand Down
1 change: 0 additions & 1 deletion .swift-version

This file was deleted.

5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@
"rust-analyzer.checkOnSave": true,
"rust-analyzer.check.allTargets": true,
"rust-analyzer.cargo.targetDir": "target/analyzer",
"rust-analyzer.cargo.extraEnv": { "MACOSX_DEPLOYMENT_TARGET": "14.2" },
"rust-analyzer.cargo.extraEnv": {
"MACOSX_DEPLOYMENT_TARGET": "14.2",
"TOOLCHAINS": "com.apple.dt.toolchain.XcodeDefault"
},
"deno.enable": true,
"deno.lint": true,
"deno.enablePaths": [
Expand Down
2 changes: 1 addition & 1 deletion crates/am2/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1 @@
swift-lib/.build
temp-resolver/
112 changes: 99 additions & 13 deletions crates/am2/build.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,101 @@
fn main() {
// Build skipped - uncomment below to re-enable Swift linking

// #[cfg(target_os = "macos")]
// {
// swift_rs::SwiftLinker::new("13.0")
// .with_package("swift-lib", "./swift-lib/")
// .link();
// }
//
// #[cfg(not(target_os = "macos"))]
// {
// println!("cargo:warning=Swift linking is only available on macOS");
// }
#[cfg(target_os = "macos")]
{
use std::path::Path;
use std::process::Command;

let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
let swift_lib_path = manifest_dir.join("swift-lib");
let frameworks_dir = swift_lib_path.join("frameworks");
let xcframework_dest = frameworks_dir.join("ArgmaxSDK.xcframework");

if !xcframework_dest.exists() {
println!("cargo:warning=ArgmaxSDK.xcframework not found, extracting from registry...");

let temp_dir = manifest_dir.join("temp-resolver");
std::fs::create_dir_all(&temp_dir).expect("Failed to create temp directory");

let resolver_package = r#"// swift-tools-version:5.10
import PackageDescription
let package = Package(
name: "resolver",
platforms: [.macOS(.v13)],
dependencies: [
.package(id: "argmaxinc.argmax-sdk-swift", exact: "1.9.3")
],
targets: []
)
"#;
std::fs::write(temp_dir.join("Package.swift"), resolver_package)
.expect("Failed to write resolver Package.swift");

let status = Command::new("swift")
.args(["package", "resolve"])
.current_dir(&temp_dir)
.status()
.expect("Failed to run swift package resolve");

if !status.success() {
panic!("swift package resolve failed");
}

let artifacts_path = temp_dir.join(
".build/artifacts/argmaxinc.argmax-sdk-swift/ArgmaxSDK/ArgmaxSDK.xcframework",
);

if !artifacts_path.exists() {
panic!(
"ArgmaxSDK.xcframework not found in artifacts at: {:?}",
artifacts_path
);
}

std::fs::create_dir_all(&frameworks_dir)
.expect("Failed to create frameworks directory");

let status = Command::new("cp")
.args([
"-R",
artifacts_path.to_str().unwrap(),
xcframework_dest.to_str().unwrap(),
])
.status()
.expect("Failed to copy xcframework");

if !status.success() {
panic!("Failed to copy ArgmaxSDK.xcframework");
}

std::fs::remove_dir_all(&temp_dir).ok();

println!("cargo:warning=ArgmaxSDK.xcframework extracted successfully");
}

let out_dir = std::env::var("OUT_DIR").unwrap();
let swift_build_dir = Path::new(&out_dir).join("swift-rs/swift-lib");
let workspace_state = swift_build_dir.join("workspace-state.json");
if workspace_state.exists() {
std::fs::remove_file(&workspace_state).ok();
}

swift_rs::SwiftLinker::new("13.0")
.with_package("swift-lib", "./swift-lib/")
.link();

let framework_path = xcframework_dest.join("macos-arm64");
println!(
"cargo:rustc-link-search=framework={}",
framework_path.display()
);
println!("cargo:rustc-link-lib=framework=ArgmaxSDK");
println!(
"cargo:rustc-link-arg=-Wl,-rpath,{}",
framework_path.display()
);
Comment on lines +85 to +94
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Hardcoded macos-arm64 breaks Intel Mac builds.

The xcframework path hardcodes macos-arm64, which will fail on Intel-based Macs. XCFrameworks contain architecture-specific slices that must be selected based on the build target.

-        let framework_path = xcframework_dest.join("macos-arm64");
+        let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_else(|_| "arm64".to_string());
+        let framework_slice = match arch.as_str() {
+            "x86_64" => "macos-x86_64",
+            "aarch64" | "arm64" => "macos-arm64",
+            _ => panic!("Unsupported architecture: {}", arch),
+        };
+        let framework_path = xcframework_dest.join(framework_slice);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let framework_path = xcframework_dest.join("macos-arm64");
println!(
"cargo:rustc-link-search=framework={}",
framework_path.display()
);
println!("cargo:rustc-link-lib=framework=ArgmaxSDK");
println!(
"cargo:rustc-link-arg=-Wl,-rpath,{}",
framework_path.display()
);
let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_else(|_| "arm64".to_string());
let framework_slice = match arch.as_str() {
"x86_64" => "macos-x86_64",
"aarch64" | "arm64" => "macos-arm64",
_ => panic!("Unsupported architecture: {}", arch),
};
let framework_path = xcframework_dest.join(framework_slice);
println!(
"cargo:rustc-link-search=framework={}",
framework_path.display()
);
println!("cargo:rustc-link-lib=framework=ArgmaxSDK");
println!(
"cargo:rustc-link-arg=-Wl,-rpath,{}",
framework_path.display()
);
🤖 Prompt for AI Agents
In crates/am2/build.rs around lines 85 to 94, the code hardcodes "macos-arm64"
which breaks Intel Mac builds; change it to select the correct xcframework slice
based on the build TARGET environment variable (std::env::var("TARGET")) —
detect whether the target contains "aarch64" (use "macos-arm64") or otherwise
use "macos-x86_64", build framework_path = xcframework_dest.join(slice), and
then use that framework_path for cargo:rustc-link-search, cargo:rustc-link-arg
rpath, and the link-lib statement so the correct architecture slice is used at
build time.

}

#[cfg(not(target_os = "macos"))]
{
println!("cargo:warning=Swift linking is only available on macOS");
}
}
37 changes: 32 additions & 5 deletions crates/am2/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,53 @@
use swift_rs::swift;
use swift_rs::{swift, SRString};

swift!(fn initialize_am2_sdk());
swift!(fn initialize_am2_sdk(api_key: &SRString));

swift!(fn check_am2_ready() -> bool);

pub fn init() {
swift!(fn transcribe_audio_file(path: &SRString) -> SRString);

pub fn init(api_key: &str) {
let key = SRString::from(api_key);
unsafe {
initialize_am2_sdk();
initialize_am2_sdk(&key);
}
}

pub fn is_ready() -> bool {
unsafe { check_am2_ready() }
}

pub fn transcribe(audio_path: &str) -> String {
let path = SRString::from(audio_path);
unsafe { transcribe_audio_file(&path).to_string() }
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_am2_swift_compilation() {
init();
let api_key = std::env::var("AM_API_KEY").expect("AM_API_KEY env var required");
init(&api_key);
assert!(is_ready());
}

#[test]
fn test_transcribe_audio() {
let api_key = std::env::var("AM_API_KEY").expect("AM_API_KEY env var required");
init(&api_key);

let audio_path = concat!(
env!("CARGO_MANIFEST_DIR"),
"/../data/src/english_1/audio.wav"
);
println!("Audio path: {}", audio_path);

let result = transcribe(audio_path);
println!("Transcription result: {}", result);

assert!(!result.is_empty());
assert!(!result.starts_with("Error:"));
}
}
3 changes: 3 additions & 0 deletions crates/am2/swift-lib/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.build/
frameworks/

18 changes: 5 additions & 13 deletions crates/am2/swift-lib/Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 10 additions & 13 deletions crates/am2/swift-lib/Package.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// swift-tools-version:6.0
// swift-tools-version:5.10

import PackageDescription

let package = Package(
name: "swift-lib",
platforms: [.macOS("13.0")],
platforms: [.macOS(.v13)],
products: [
.library(
name: "swift-lib",
Expand All @@ -15,24 +15,21 @@ let package = Package(
.package(
url: "https://github.com/Brendonovich/swift-rs",
revision: "01980f981bc642a6da382cc0788f18fdd4cde6df"),
.package(
id: "argmaxinc.argmax-sdk-swift",
exact: "1.9.3"),
.package(
url: "https://github.com/huggingface/swift-transformers.git",
exact: "0.1.24")
.package(url: "https://github.com/argmaxinc/WhisperKit.git", exact: "0.14.1")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Verify WhisperKit version 0.14.1 is current and stable.

Using exact version constraint ensures reproducibility but may cause dependency resolution issues if WhisperKit's transitive dependencies conflict. Consider using from: "0.14.1" if flexibility is acceptable.


🌐 Web query:

WhisperKit 0.14.1 release argmaxinc latest version

💡 Result:

WhisperKit v0.14.1 was released on Oct 17, 2025. The latest release is v0.15.0 (marked "Latest") published on Nov 7, 2025. [1]

[1] argmaxinc/WhisperKit releases on GitHub.


WhisperKit 0.14.1 is outdated; consider upgrading to 0.15.0.

WhisperKit v0.14.1 was released on Oct 17, 2025, but the latest release is v0.15.0 (published Nov 7, 2025). Using exact version pinning on 0.14.1 locks the dependency to an older release. Unless there's a specific reason to avoid 0.15.0, consider updating to the latest stable version or switching to from: "0.14.1" to allow flexibility for security patches and bug fixes.

🤖 Prompt for AI Agents
In crates/am2/swift-lib/Package.swift around line 18, the dependency is pinned
to WhisperKit exact "0.14.1" which is outdated; update the package declaration
to either use the new exact version "0.15.0" (replace exact: "0.14.1" with
exact: "0.15.0") or relax the pin to allow patch updates (replace exact:
"0.14.1" with from: "0.14.1") depending on whether you want a strict upgrade to
0.15.0 or flexible updates for future patches.

],
targets: [
.binaryTarget(
name: "ArgmaxSDK",
path: "frameworks/ArgmaxSDK.xcframework"
),
Comment on lines +21 to +24
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify ArgmaxSDK.xcframework exists at the specified path.

Binary targets require the xcframework to be present at build time. Ensure frameworks/ArgmaxSDK.xcframework is either committed to the repository or downloaded as part of the build process.


🏁 Script executed:

#!/bin/bash
# Description: Verify ArgmaxSDK.xcframework exists at the expected location

# Check if the xcframework directory exists
fd -t d "ArgmaxSDK.xcframework" crates/am2/swift-lib

# Also check if there's a download script or documentation for obtaining the framework
fd -e sh -e md . crates/am2/swift-lib | xargs -I {} sh -c 'echo "=== {} ===" && head -30 {}'

Length of output: 0



🏁 Script executed:

#!/bin/bash
# Description: Comprehensive verification of ArgmaxSDK.xcframework and related files

echo "=== Checking directory structure ==="
ls -la crates/am2/swift-lib/ 2>/dev/null || echo "crates/am2/swift-lib does not exist"

echo -e "\n=== Searching for ArgmaxSDK.xcframework anywhere in repo ==="
find . -name "*ArgmaxSDK*" -o -name "*.xcframework" 2>/dev/null | head -20

echo -e "\n=== Checking .gitignore for binary/framework patterns ==="
cat .gitignore 2>/dev/null | grep -E "(\.xcframework|frameworks|binary|\.xcarchive)" || echo "No matching patterns in .gitignore"

echo -e "\n=== Checking Package.swift file context ==="
cat crates/am2/swift-lib/Package.swift 2>/dev/null || echo "Package.swift not found"

echo -e "\n=== Looking for build/download scripts in swift-lib ==="
find crates/am2/swift-lib -type f \( -name "*.sh" -o -name "Makefile" -o -name "*.yaml" -o -name "*.yml" \) 2>/dev/null

echo -e "\n=== Searching for ArgmaxSDK mentions in repo ==="
rg -i "ArgmaxSDK" --type swift --type text -B2 -A2 | head -40

Length of output: 1548


🏁 Script executed:

#!/bin/bash
# Description: Comprehensive verification of ArgmaxSDK.xcframework and related files

echo "=== Checking directory structure ==="
ls -la crates/am2/swift-lib/ 2>/dev/null || echo "crates/am2/swift-lib does not exist"

echo -e "\n=== Searching for ArgmaxSDK.xcframework anywhere in repo ==="
find . -name "*ArgmaxSDK*" -o -name "*.xcframework" 2>/dev/null | head -20

echo -e "\n=== Checking .gitignore for binary/framework patterns ==="
cat .gitignore 2>/dev/null | grep -E "(\.xcframework|frameworks|binary|\.xcarchive)" || echo "No matching patterns in .gitignore"

echo -e "\n=== Checking Package.swift file context ==="
cat crates/am2/swift-lib/Package.swift 2>/dev/null || echo "Package.swift not found"

echo -e "\n=== Looking for build/download scripts in swift-lib ==="
find crates/am2/swift-lib -type f \( -name "*.sh" -o -name "Makefile" -o -name "*.yaml" -o -name "*.yml" \) 2>/dev/null

echo -e "\n=== Searching for ArgmaxSDK mentions in repo ==="
rg -i "ArgmaxSDK" --type swift --type text -B2 -A2 | head -40

Length of output: 1548


Add frameworks directory with ArgmaxSDK.xcframework or remove the binary target dependency.

The frameworks/ArgmaxSDK.xcframework referenced in Package.swift (lines 21-24) does not exist in the repository. The frameworks/ directory is missing entirely, and no build scripts or download mechanisms are present. When developers attempt to build this package, the binary target resolution will fail. Either:

  • Commit the xcframework to the repository under frameworks/ArgmaxSDK.xcframework, or
  • Remove the binaryTarget from Package.swift and the dependency from the swift-lib target
🤖 Prompt for AI Agents
In crates/am2/swift-lib/Package.swift around lines 21 to 24, Package.swift
declares a binaryTarget pointing to frameworks/ArgmaxSDK.xcframework which is
missing from the repo; fix by either adding the missing
frameworks/ArgmaxSDK.xcframework into the repository (create the frameworks/
directory, commit the ArgmaxSDK.xcframework bundle, and verify the path and
xcframework structure) or remove the binaryTarget entry and any references to
"ArgmaxSDK" from the package's target dependencies and targets list so the
package no longer expects that binary; ensure Package.swift and any swift-lib
target dependency lists are updated accordingly and run swift build to verify
resolution succeeds.

.target(
name: "swift-lib",
dependencies: [
.product(name: "SwiftRs", package: "swift-rs"),
.product(name: "Argmax", package: "argmaxinc.argmax-sdk-swift")
.product(name: "WhisperKit", package: "WhisperKit"),
"ArgmaxSDK"
],
path: "src",
swiftSettings: [
.swiftLanguageMode(.v5)
]
path: "src"
)
]
)
Expand Down
49 changes: 45 additions & 4 deletions crates/am2/swift-lib/src/lib.swift
Original file line number Diff line number Diff line change
@@ -1,17 +1,58 @@
import Argmax
import ArgmaxSDK
import Foundation
import SwiftRs

private var isAM2Ready = false
private var whisperKitPro: WhisperKitPro?

@_cdecl("initialize_am2_sdk")
public func initialize_am2_sdk() {
isAM2Ready = true
print("AM2 SDK initialized successfully")
public func initialize_am2_sdk(apiKey: SRString) {
let key = apiKey.toString()
let semaphore = DispatchSemaphore(value: 0)

Task {
await ArgmaxSDK.with(ArgmaxConfig(apiKey: key))
isAM2Ready = true
print("AM2 SDK initialized successfully with API key: \(key.prefix(10))...")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Avoid logging API key prefixes.

Logging even the first 10 characters of an API key could expose sensitive information in logs. Consider removing or masking this entirely.

-    print("AM2 SDK initialized successfully with API key: \(key.prefix(10))...")
+    print("AM2 SDK initialized successfully")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
print("AM2 SDK initialized successfully with API key: \(key.prefix(10))...")
print("AM2 SDK initialized successfully")
🤖 Prompt for AI Agents
In crates/am2/swift-lib/src/lib.swift around line 16, the initialization log
prints the first 10 characters of the API key which risks leaking sensitive
data; remove the key prefix from the log and replace it with a non-sensitive
message (e.g., "AM2 SDK initialized successfully") or a fully masked token
(e.g., "<redacted>") so no part of the API key is written to logs.

semaphore.signal()
}

semaphore.wait()
}

@_cdecl("check_am2_ready")
public func check_am2_ready() -> Bool {
return isAM2Ready
}

@_cdecl("transcribe_audio_file")
public func transcribe_audio_file(path: SRString) -> SRString {
let audioPath = path.toString()
print("Transcribing: \(audioPath)")

var result = ""

let semaphore = DispatchSemaphore(value: 0)

Task {
do {
let config = WhisperKitProConfig(model: "large-v3-v20240930_626MB")
let kit = try await WhisperKitPro(config)
whisperKitPro = kit

print("WhisperKitPro initialized, starting transcription...")

let results = try await kit.transcribe(audioPath: audioPath)
let transcript = WhisperKitProUtils.mergeTranscriptionResults(results).text
result = transcript
print("Transcription complete: \(result)")
} catch {
result = "Error: \(error.localizedDescription)"
print("Transcription error: \(error)")
}
semaphore.signal()
}

semaphore.wait()
return SRString(result)
}
Loading