diff --git a/README.md b/README.md
index 5f2577e..a340141 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@
Today's AI assistants are *too slow*, *too basic*, or *too disconnected* from real user needs.
**NbAIl** solves this by combining **super-fast text, smooth voice conversation, intelligent command execution, and planned augmented reality (AR) capabilities** — all in a **single multimodal powerhouse**.
-One of NbAIl’s proudest innovations: **Command Mode**.
+One of NbAIl's proudest innovations: **Command Mode**.
Simply type a command like `/open notepad and write hello world`, and our **Terminator Agent**, powered by **Groq** for understanding and **local Python automation**, will **open system apps, type messages, and perform actions** automatically.
**Zero manual work. Full smart automation.**
Built to behave like a *true futuristic assistant* — not just a chatbot.
@@ -68,7 +68,7 @@ Built to behave like a *true futuristic assistant* — not just a chatbot.
- ✅ **Multi-mode Switcher** — instantly switch between chat, voice, command, and AR
- ✅ **Error Handling & Fail-Safes** — graceful recovery when apps not found
-🚀 **NbAIl doesn’t just "chat." It "acts."**
+🚀 **NbAIl doesn't just "chat." It "acts."**
---
@@ -125,7 +125,7 @@ python terminator_agent.py
```bash
ngrok http 8000
-(Replace 5000 with your local port if it’s different.)
+(Replace 5000 with your local port if it's different.)
```
—
@@ -147,7 +147,7 @@ ngrok http 8000
# 🔮 Future Scope of NbAIl
While NbAIl already delivers a futuristic multimodal experience, the journey has just begun!
-Here’s what’s planned for future versions:
+Here's what's planned for future versions:
---
@@ -190,7 +190,7 @@ Here’s what’s planned for future versions:
# 📚 Resources / Credits
This project would not have been possible without the amazing technologies, APIs, and open-source tools available to the community.
-Here’s what powered NbAIl:
+Here's what powered NbAIl:
---
@@ -254,3 +254,29 @@ Bigger, smarter, and even crazier updates are on the way.
> **"The ones who are crazy enough to think they can change the world are the ones who do."**
> — Apple, 1997
+
+## Environment Variables
+
+You'll need to set up the following environment variables in your `.env.local` file:
+
+- `NEXT_PUBLIC_GROQ_API_KEY`: Your Groq API key for AI analysis
+- `NEXT_PUBLIC_MISTRAL_API_KEY`: Your Mistral API key for alternative AI analysis (optional)
+
+### Obtaining API Keys
+
+1. **Groq API Key**:
+ - Visit [Groq's website](https://console.groq.com/)
+ - Create an account and generate an API key
+
+2. **Mistral API Key**:
+ - Visit [Mistral's website](https://mistral.ai/)
+ - Create an account and generate an API key
+ - Note: We're using the Pixtral 12B 2409 model for AR mode vision analysis
+
+Example `.env.local` file:
+```
+NEXT_PUBLIC_GROQ_API_KEY=your_groq_api_key_here
+NEXT_PUBLIC_MISTRAL_API_KEY=your_mistral_api_key_here
+```
+
+**Note**: Keep your API keys confidential and never commit them to version control.
diff --git a/app/ar-mode/MobileCarousel.tsx b/app/ar-mode/MobileCarousel.tsx
new file mode 100644
index 0000000..5bda835
--- /dev/null
+++ b/app/ar-mode/MobileCarousel.tsx
@@ -0,0 +1,120 @@
+import { AnimatePresence, motion } from "framer-motion";
+import Carousel from "@/components/ui/carousel-new"; // Updated import path
+import { FiLayers, FiCode, FiFileText } from "react-icons/fi";
+
+// Define the slide data structure that matches arModeSlides
+interface SlideData {
+ title: string;
+ button: string;
+ description: string;
+ src?: string;
+}
+
+interface MobileCarouselProps {
+ showCards: boolean;
+ slides?: SlideData[];
+}
+
+const MobileCarousel = ({ showCards, slides }: MobileCarouselProps) => {
+ // Default slide data if none is provided
+ const defaultSlides = [
+ {
+ title: "AR Insights",
+ description: "Explore AI-powered scene analysis",
+ id: 1,
+ icon: ,
+ },
+ {
+ title: "Identity Snapshot",
+ description: "Demographic and emotional insights about detected individuals.",
+ button: "View Traits",
+ src: "/images/identity-analysis.jpg",
+ id: 2,
+ icon: ,
+ },
+ {
+ title: "Object Detection",
+ description: "Detected Objects: - None\nDetected Gestures: - None",
+ button: "Recognize Items",
+ id: 3,
+ icon: ,
+ }
+ ];
+
+ // Convert the slides from arModeSlides format to CarouselItem format if slides are provided
+ const carouselItems = slides ? slides.map((slide, index) => {
+ let icon;
+ // Assign icons based on slide title or index
+ if (slide.title.includes("Scene")) {
+ icon = ;
+ } else if (slide.title.includes("Identity")) {
+ icon = ;
+
+ // If this is the Identity Snapshot slide, parse the traits
+ if (slide.description && slide.description.includes('\n')) {
+ const traitPills = slide.description.split('\n')
+ .filter(trait => trait.trim() !== '')
+ .map(trait => {
+ let icon = '🤔';
+ if (trait.includes('Age')) icon = '🎂';
+ if (trait.includes('Gender')) icon = '👤';
+ if (trait.includes('Mood')) icon = '😀';
+ return `${icon} ${trait.replace('- ', '')}`;
+ });
+
+ // Update description with trait information
+ slide.description = traitPills.length > 0
+ ? traitPills.join('\n')
+ : 'No Clear Face Detected. Check the lighting';
+ }
+ } else if (slide.title.includes("Object")) {
+ icon = ;
+
+ // Directly set the description to match desktop view card
+ slide.description = `**Objects & Gestures:**
+- No objects detected
+- No gestures detected`;
+ }
+
+ return {
+ title: slide.title,
+ description: slide.description,
+ id: index + 1,
+ icon: icon || , // Ensure icon is always defined
+ image: slide.title.includes("Identity") ? slide.src : undefined, // Only add image for Identity Snapshot
+ data: undefined // Remove data for all slides
+ };
+ }) : defaultSlides;
+
+ return (
+ // Only show on mobile devices (md:hidden)
+
+
+
+ {showCards && (
+
+
+
+
+
+ )}
+
+
+
+ );
+};
+
+export default MobileCarousel;
\ No newline at end of file
diff --git a/app/ar-mode/ar-mode-content.tsx b/app/ar-mode/ar-mode-content.tsx
index e52ee69..ea338b1 100644
--- a/app/ar-mode/ar-mode-content.tsx
+++ b/app/ar-mode/ar-mode-content.tsx
@@ -5,12 +5,15 @@ import { useRouter } from "next/navigation"
import { Button } from "@/components/ui/button"
import { ArrowLeft, Camera, Mic, Loader2, AlertTriangle, Volume2, X, RefreshCw, CircleDot, ArrowRight } from "lucide-react"
import { motion, AnimatePresence } from "framer-motion"
-// Import the new service function
-import { getGroqVisionAnalysis, getGroqTranscription } from "@/lib/groq-service"
+// Import the new service function and provider enum
+import { getGroqVisionAnalysis, getGroqTranscription, AIProvider } from "@/lib/groq-service"
import { speakText } from "@/lib/tts-service"
import ReactMarkdown from 'react-markdown'
import InfoWidget from '@/components/ar/InfoWidget'
import React from 'react'
+import Carousel from "@/components/ui/carousel-new"
+import { FiLayers, FiCode, FiFileText } from 'react-icons/fi'
+import MobileCarousel from './MobileCarousel' // Import the MobileCarousel component
// THESE IMPORTS ARE LIKELY NEEDED BASED ON LINTER ERRORS - ADDING THEM HERE
import * as faceapi from 'face-api.js';
@@ -57,6 +60,7 @@ export default function ARModeContent() { // Renamed from ARModePage
const captureIntervalRef = useRef(null);
const [recordStartTime, setRecordStartTime] = useState(null);
const [isRecording, setIsRecording] = useState(false);
+ const [aiProvider, setAiProvider] = useState(AIProvider.GROQ);
// Face-API model loading options
@@ -410,7 +414,7 @@ export default function ARModeContent() { // Renamed from ARModePage
}
try {
- const response = await getGroqVisionAnalysis(imageDataUrl, DETAILED_BASE_PROMPT);
+ const response = await getGroqVisionAnalysis(imageDataUrl, DETAILED_BASE_PROMPT, aiProvider);
setAiResponse(response);
console.log("[captureAndAnalyzeFrame] Groq vision analysis complete. Response:", response);
} catch (err) {
@@ -432,7 +436,8 @@ export default function ARModeContent() { // Renamed from ARModePage
setAiResponse,
setShowCards,
setLastAnalysisTime,
- videoRef // videoRef.current is used by analysis helpers
+ videoRef,
+ aiProvider
]);
// Effect to handle recording logic (timer and periodic capture)
@@ -565,7 +570,7 @@ export default function ARModeContent() { // Renamed from ARModePage
try {
// Using DETAILED_BASE_PROMPT for manual analysis as well
- const response = await getGroqVisionAnalysis(imageDataUrl, DETAILED_BASE_PROMPT)
+ const response = await getGroqVisionAnalysis(imageDataUrl, DETAILED_BASE_PROMPT, aiProvider)
setAiResponse(response)
} catch (err) {
console.error("Error analyzing image with Groq (handleAnalyzeImage):", err);
@@ -577,7 +582,7 @@ export default function ARModeContent() { // Renamed from ARModePage
setShowCards(true); // Show all cards after analyses are attempted
setIsAnalyzing(false);
setStatusMessage(null);
- }, [modelsReady, mediaPipeModelsReady, captureFrame, DETAILED_BASE_PROMPT, performFaceApiAnalysis, performMediaPipeAnalysis, getGroqVisionAnalysis, setAiResponse, setCapturedImagePreviewUrl, setObjectCardContent, setIdentityInfoContent, setError, setFaceApiError, setMediaPipeError, setShowCards, setStatusMessage, videoRef]);
+ }, [modelsReady, mediaPipeModelsReady, captureFrame, DETAILED_BASE_PROMPT, performFaceApiAnalysis, performMediaPipeAnalysis, getGroqVisionAnalysis, setAiResponse, setCapturedImagePreviewUrl, setObjectCardContent, setIdentityInfoContent, setError, setFaceApiError, setMediaPipeError, setShowCards, setStatusMessage, videoRef, aiProvider]);
// --- Flip Camera ---
@@ -778,7 +783,7 @@ export default function ARModeContent() { // Renamed from ARModePage
try {
// Send the combined prompt to the vision analysis function
- const response = await getGroqVisionAnalysis(imageDataUrl, finalPrompt)
+ const response = await getGroqVisionAnalysis(imageDataUrl, finalPrompt, aiProvider)
setAiResponse(response)
setStatusMessage("Speaking response...")
// Speak the response
@@ -813,7 +818,7 @@ export default function ARModeContent() { // Renamed from ARModePage
setIsAnalyzing(false);
setShowCards(true); // Show cards after all analysis attempts (might be empty if errors)
}
- }, [captureFrame, modelsReady, mediaPipeModelsReady, DETAILED_BASE_PROMPT, performFaceApiAnalysis, performMediaPipeAnalysis, getGroqVisionAnalysis, speakText, videoRef, setCapturedImagePreviewUrl, setObjectCardContent, setIdentityInfoContent, setAiResponse, setError, setFaceApiError, setMediaPipeError, setShowCards, setStatusMessage, setIsAnalyzing, setIsSpeaking]);
+ }, [captureFrame, modelsReady, mediaPipeModelsReady, DETAILED_BASE_PROMPT, performFaceApiAnalysis, performMediaPipeAnalysis, getGroqVisionAnalysis, speakText, videoRef, setCapturedImagePreviewUrl, setObjectCardContent, setIdentityInfoContent, setAiResponse, setError, setFaceApiError, setMediaPipeError, setShowCards, setStatusMessage, setIsAnalyzing, setIsSpeaking, aiProvider]);
// Main prediction loop and drawing logic
useEffect(() => {
@@ -861,23 +866,43 @@ export default function ARModeContent() { // Renamed from ARModePage
// ... existing code ...
}, []);
+ // Prepare slides data for the new Carousel
+ const arModeSlides = [
+ {
+ title: "Scene Analysis",
+ button: "Explore Insights",
+ description: aiResponse || "Detailed description of the surrounding environment, capturing key visual elements and context.",
+ },
+ {
+ title: "Identity SnapShot",
+ button: "View Traits",
+ src: capturedImagePreviewUrl || "/images/identity-analysis.jpg",
+ description: identityInfoContent || "Demographic and emotional insights about detected individuals."
+ },
+ {
+ title: "Object Detection",
+ button: "Recognize Items",
+ src: "/images/object-detection.jpg",
+ description: objectCardContent || "Comprehensive list of detected objects and gestures in the scene."
+ }
+ ];
+
return (
// Conditional rendering based on isClient is handled by the early return
// So, this JSX assumes it's client-side.