stephenwahogo commited on
Commit
1665036
·
verified ·
1 Parent(s): 550f3c8

Upload nicto_ai\voice\backend_interface.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. nicto_ai//voice//backend_interface.py +188 -0
nicto_ai//voice//backend_interface.py ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ NICTO AI - LLM Backend Interface
3
+ Abstract interface for LLM backends.
4
+
5
+ The agent_loop.py uses this interface to communicate with
6
+ LLM providers without being coupled to any specific API.
7
+ """
8
+
9
+ from abc import ABC, abstractmethod
10
+ from typing import Dict, List, Optional
11
+ from dataclasses import dataclass, field
12
+
13
+
14
+ @dataclass
15
+ class Message:
16
+ """A chat message"""
17
+ role: str # "system", "user", "assistant"
18
+ content: str
19
+ metadata: Dict = field(default_factory=dict)
20
+
21
+
22
+ @dataclass
23
+ class CompletionResult:
24
+ """Result from LLM completion"""
25
+ text: str
26
+ model: str = ""
27
+ usage: Dict = field(default_factory=dict) # tokens in/out
28
+ finish_reason: str = "stop"
29
+ metadata: Dict = field(default_factory=dict)
30
+
31
+
32
+ class LLMBackend(ABC):
33
+ """
34
+ Abstract LLM backend interface.
35
+
36
+ Implement this interface to connect NICTO's voice system
37
+ to any LLM provider (Claude, GPT-4, NICTO's own model, etc.).
38
+
39
+ Example:
40
+ class ClaudeBackend(LLMBackend):
41
+ def complete(self, messages, **kwargs):
42
+ # Call Anthropic API
43
+ return CompletionResult(text=response)
44
+ """
45
+
46
+ @abstractmethod
47
+ def complete(
48
+ self,
49
+ messages: List[Message],
50
+ temperature: float = 0.7,
51
+ max_tokens: int = 1024,
52
+ system_prompt: Optional[str] = None,
53
+ **kwargs,
54
+ ) -> CompletionResult:
55
+ """
56
+ Generate a completion from a list of messages.
57
+
58
+ Args:
59
+ messages: Conversation history
60
+ temperature: Sampling temperature
61
+ max_tokens: Maximum tokens to generate
62
+ system_prompt: System prompt (optional)
63
+
64
+ Returns:
65
+ CompletionResult with generated text
66
+ """
67
+ pass
68
+
69
+ @abstractmethod
70
+ def is_available(self) -> bool:
71
+ """Check if this backend is available and configured"""
72
+ pass
73
+
74
+ @property
75
+ @abstractmethod
76
+ def name(self) -> str:
77
+ """Backend name"""
78
+ pass
79
+
80
+
81
+ class ClaudeBackend(LLMBackend):
82
+ """Anthropic Claude API backend"""
83
+
84
+ def __init__(self, api_key: Optional[str] = None, model: str = "claude-3-sonnet-20240229"):
85
+ self.api_key = api_key
86
+ self.model = model
87
+
88
+ def complete(self, messages, temperature=0.7, max_tokens=1024, system_prompt=None, **kwargs):
89
+ import requests
90
+ import os
91
+
92
+ api_key = self.api_key or os.environ.get("ANTHROPIC_API_KEY")
93
+ if not api_key:
94
+ return CompletionResult(text="", finish_reason="error")
95
+
96
+ headers = {
97
+ "x-api-key": api_key,
98
+ "anthropic-version": "2023-06-01",
99
+ "content-type": "application/json",
100
+ }
101
+
102
+ api_messages = []
103
+ if system_prompt:
104
+ api_messages.append({"role": "system", "content": system_prompt})
105
+
106
+ for msg in messages:
107
+ if msg.role in ("user", "assistant"):
108
+ api_messages.append({"role": msg.role, "content": msg.content})
109
+
110
+ payload = {
111
+ "model": self.model,
112
+ "messages": api_messages,
113
+ "max_tokens": max_tokens,
114
+ "temperature": temperature,
115
+ }
116
+
117
+ try:
118
+ response = requests.post(
119
+ "https://api.anthropic.com/v1/messages",
120
+ headers=headers,
121
+ json=payload,
122
+ timeout=60,
123
+ )
124
+
125
+ if response.status_code == 200:
126
+ data = response.json()
127
+ text = data.get("content", [{}])[0].get("text", "")
128
+ return CompletionResult(
129
+ text=text,
130
+ model=data.get("model", self.model),
131
+ usage=data.get("usage", {}),
132
+ )
133
+ else:
134
+ return CompletionResult(text="", finish_reason="error")
135
+ except Exception as e:
136
+ return CompletionResult(text="", finish_reason=f"error: {e}")
137
+
138
+ def is_available(self):
139
+ import os
140
+ return bool(self.api_key or os.environ.get("ANTHROPIC_API_KEY"))
141
+
142
+ @property
143
+ def name(self):
144
+ return "claude"
145
+
146
+
147
+ class NICTOBackend(LLMBackend):
148
+ """NICTO's own model backend (stub - wired to NICTOModel when checkpoint ready)"""
149
+
150
+ def __init__(self, model=None):
151
+ self._model = model
152
+
153
+ def complete(self, messages, temperature=0.7, max_tokens=1024, system_prompt=None, **kwargs):
154
+ if self._model is None:
155
+ return CompletionResult(
156
+ text="[NICTO model not loaded. Connect a trained checkpoint to use NICTOBackend.]",
157
+ finish_reason="stub",
158
+ )
159
+
160
+ # When model is available, this would:
161
+ # 1. Tokenize messages
162
+ # 2. Run model.generate()
163
+ # 3. Decode and return
164
+ return CompletionResult(text="[NICTOBackend: model connected but generation not implemented yet]")
165
+
166
+ def is_available(self):
167
+ return self._model is not None
168
+
169
+ @property
170
+ def name(self):
171
+ return "nicto"
172
+
173
+
174
+ class EchoBackend(LLMBackend):
175
+ """Simple echo backend for testing"""
176
+
177
+ def complete(self, messages, **kwargs):
178
+ if messages:
179
+ last = messages[-1]
180
+ return CompletionResult(text=f"Echo: {last.content}")
181
+ return CompletionResult(text="")
182
+
183
+ def is_available(self):
184
+ return True
185
+
186
+ @property
187
+ def name(self):
188
+ return "echo"