Skip to content

Commit

Permalink
Work!
Browse files Browse the repository at this point in the history
  • Loading branch information
QuinnDamerell committed Jan 10, 2025
1 parent a4cf45c commit 8c98a56
Show file tree
Hide file tree
Showing 7 changed files with 38 additions and 15 deletions.
2 changes: 1 addition & 1 deletion homeway/homeway_linuxhost/linuxhost.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ def RunBlocking(self, storageDir, versionFileDir, devConfig_CanBeNone):
configManager.UpdateConfigIfNeeded()

# Setup the sage sub system, it won't be started until the primary connection is established.
self.Sage = SageHost(self.Logger, devLocalHomewayServerAddress_CanBeNone)
self.Sage = SageHost(self.Logger, pluginVersionStr, devLocalHomewayServerAddress_CanBeNone)

# Now start the main runner!
pluginConnectUrl = HostCommon.GetPluginConnectionUrl()
Expand Down
17 changes: 14 additions & 3 deletions homeway/homeway_linuxhost/sage/fabric.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,20 @@

from homeway.sentry import Sentry
from homeway.websocketimpl import Client
from homeway.pingpong import PingPong

# Manages the Sage Fabric connection with the Homeway service.
class Fabric:

# For debugging, it's too chatty to enable always.
c_LogWsMessages = False

def __init__(self, logger:logging.Logger, fiberManager, pluginId:str, apiKey:str, devLocalHomewayServerAddress_CanBeNone:str) -> None:
def __init__(self, logger:logging.Logger, fiberManager, pluginId:str, apiKey:str, addonVersion:str, devLocalHomewayServerAddress_CanBeNone:str) -> None:
self.Logger = logger
self.FiberManager = fiberManager
self.PrinterId = pluginId
self.ApiKey = apiKey
self.AddonVersion = addonVersion
self.DevLocalHomewayServerAddress_CanBeNone = devLocalHomewayServerAddress_CanBeNone

# The current websocket connection and Id
Expand Down Expand Up @@ -99,8 +101,16 @@ def _ConnectionThread(self):
def Closed(ws:Client):
self.Logger.info(f"{self._getLogTag()} Websocket closed")

# Get the URI, allow for local dev overrides.
uri = "wss://hw-sage-v1.homeway.io/sage-fabric-websocket"
# Get the subdomain to use. If possible, we want to use the low latency server.
# If this is our first reconnect, then we should try to lowest latency server.
subdomain = "hw-sage-v1"
if self.ConId == 1:
lowestLatencySub = PingPong.Get().GetLowestLatencyServerSub()
if lowestLatencySub is not None:
subdomain = lowestLatencySub

# Build the full URL, allow the dev config to override it.
uri = f"wss://{subdomain}.homeway.io/sage-fabric-websocket"
if self.DevLocalHomewayServerAddress_CanBeNone is not None and len(self.DevLocalHomewayServerAddress_CanBeNone) > 0:
self.Logger.info(f"{self._getLogTag()} Using dev local server address [{self.DevLocalHomewayServerAddress_CanBeNone}]")
uri = f"ws://{self.DevLocalHomewayServerAddress_CanBeNone}/sage-fabric-websocket"
Expand All @@ -109,6 +119,7 @@ def Closed(ws:Client):
headers = {}
headers["X-Plugin-Id"] = self.PrinterId
headers["X-Api-Key"] = self.ApiKey
headers["x-Addon-Version"] = self.AddonVersion

# Start the websocket.
self.Logger.info(f"{self._getLogTag()} Starting fabric connection to [{uri}]")
Expand Down
12 changes: 8 additions & 4 deletions homeway/homeway_linuxhost/sage/fibermanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ async def onDataStreamReceived(statusCode:int, data:bytearray, dataContext:SageD
raise Exception("Sage Listen got a response that wasn't text?")

# Set the text.
# Remember that an empty buffer isn't a failure, it means there were no words in the text!
response.Text = data.decode("utf-8")
return True

Expand All @@ -119,6 +120,7 @@ async def onDataStreamReceived(statusCode:int, data:bytearray, dataContext:SageD
return ""

# If we are here, this was the blocking request to get the result, so this should always be set.
# Remember that an empty buffer isn't a failure, it means there were no words in the text!
if response.Text is None:
self.Logger.error("Sage Listen didn't fail the status code but has no text?")
return None
Expand Down Expand Up @@ -527,14 +529,16 @@ def OnIncomingMessage(self, buf:bytearray):

if context.StatusCode is None:
# This is the first (and possibly only) response.
# These are required.
# The status code is always required.
statusCode = msg.StatusCode()
dataContext = msg.DataContext()
if statusCode is None or statusCode <= 0:
raise Exception(f"Sage Fiber message has an invalid status code. {statusCode}")
if dataContext is None:
raise Exception("Sage Fiber message has is missing the data context.")
context.StatusCode = statusCode

# The data context is always required as well, unless it's a close message.
dataContext = msg.DataContext()
if dataContext is None and isDataTransmissionDone is False:
raise Exception("Sage Fiber message has is missing the data context.")
context.DataContext = dataContext

# Append the data.
Expand Down
12 changes: 9 additions & 3 deletions homeway/homeway_linuxhost/sage/sagehandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,12 @@ def getAttribution(d:dict) -> Attribution:
# Build the info object from the response.
info = Info()

# This is a list of languages we support.
# We keep them here on the client side so they don't have to be sent every time.
# Most of our models are multi-lingual, so most of all of these work with any given voice or model.
# For anything that isn't supported, the server will map it on demand.
sageLanguages = ["af-ZA", "am-ET", "ar-AE","ar-BH","ar-DZ","ar-EG","ar-IL","ar-IQ","ar-JO","ar-KW","ar-LB","ar-MA","ar-MR","ar-OM","ar-PS","ar-QA","ar-SA","ar-SY","ar-TN","ar-YE","az-AZ","bg-BG","bn-BD","bn-IN","bs-BA","ca-ES","cmn-Hans-CN","cmn-Hans-HK","cmn-Hant-TW","cs-CZ","da-DK","de-AT","de-CH","de-DE","el-GR","en-AU","en-CA","en-GB","en-GH","en-HK","en-IE","en-IN","en-KE","en-NG","en-NZ","en-PH","en-PK","en-SG","en-TZ","en-US","en-ZA","es-AR","es-BO","es-CL","es-CO","es-CR","es-DO","es-EC","es-ES","es-GT","es-HN","es-MX","es-NI","es-PA","es-PE","es-PR","es-PY","es-SV","es-US","es-UY","es-VE","et-EE","eu-ES","fa-IR","fi-FI","fil-PH","fr-BE","fr-CA","fr-CH","fr-FR","gl-ES","gu-IN","hi-IN","hr-HR","hu-HU","hy-AM","id-ID","is-IS","it-CH","it-IT","iw-IL","ja-JP","jv-ID","ka-GE","kk-KZ","km-KH","kn-IN","ko-KR","lo-LA","lt-LT","lv-LV","mk-MK","ml-IN","mn-MN","mr-IN","ms-MY","my-MM","ne-NP","nl-BE","nl-NL","no-NO","pa-Guru-IN","pl-PL","pt-BR","pt-PT","ro-RO","ru-RU","si-LK","sk-SK","sl-SI","sq-AL","sr-RS","su-ID","sv-SE","sw-KE","sw-TZ","ta-IN","ta-LK","ta-MY","ta-SG","te-IN","th-TH","tr-TR","uk-UA","ur-IN","ur-PK","uz-UZ","vi-VN","yue-Hant-HK","zu-ZA",]

# Get the AsrPrograms - Speech to Text
info.asr = []
for p in getOrThrow(result, "SpeechToText", list):
Expand All @@ -255,7 +261,7 @@ def getAttribution(d:dict) -> Attribution:
name=getOrThrow(m, "Name", str),
description=getOrThrow(m, "Description", str),
version=getOrThrow(m, "Version", str),
languages=getOrThrow(m, "Languages", list),
languages=sageLanguages,
attribution=getAttribution(m),
installed=True
)
Expand All @@ -278,7 +284,7 @@ def getAttribution(d:dict) -> Attribution:
name=getOrThrow(m, "Name", str),
description=getOrThrow(m, "Description", str),
version=getOrThrow(m, "Version", str),
languages=getOrThrow(m, "Languages", list),
languages=sageLanguages,
attribution=getAttribution(m),
installed=True
)
Expand All @@ -302,7 +308,7 @@ def getAttribution(d:dict) -> Attribution:
name=getOrThrow(m, "Name", str),
description=getOrThrow(m, "Description", str),
version=getOrThrow(m, "Version", str),
languages=getOrThrow(m, "Languages", list),
languages=sageLanguages,
attribution=getAttribution(m),
installed=True
)
Expand Down
2 changes: 1 addition & 1 deletion homeway/homeway_linuxhost/sage/sagehistory.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
class SageHistory:

# The max number of history items to keep.
c_MaxHistoryItems = 40
c_MaxHistoryItems = 25

# The max age of a history item in seconds.
# This is a tricky value, because we don't know if the user closed and opened a new chat.
Expand Down
5 changes: 3 additions & 2 deletions homeway/homeway_linuxhost/sage/sagehost.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ class SageHost:
# Maybe this should be dynamic to support multiple instances, but it can't change after it's been discovered.
c_ServerPort = 11027

def __init__(self, logger:logging.Logger, devLocalHomewayServerAddress_CanBeNone:str):
def __init__(self, logger:logging.Logger, addonVersion:str, devLocalHomewayServerAddress_CanBeNone:str):
self.Logger = logger
self.AddonVersion = addonVersion
self.DevLocalHomewayServerAddress_CanBeNone = devLocalHomewayServerAddress_CanBeNone
self.PluginId:str = None
self.ApiKey:str = None
Expand Down Expand Up @@ -51,7 +52,7 @@ def StartOrRefresh(self, pluginId:str, apiKey:str):

# This is the first run, get things going.
self.FiberManager = FiberManager(self.Logger)
self.Fabric = Fabric(self.Logger, self.FiberManager, self.PluginId, self.ApiKey, self.DevLocalHomewayServerAddress_CanBeNone)
self.Fabric = Fabric(self.Logger, self.FiberManager, self.PluginId, self.ApiKey, self.AddonVersion, self.DevLocalHomewayServerAddress_CanBeNone)
self.FiberManager.SetFabric(self.Fabric)
self.Fabric.Start()

Expand Down
3 changes: 2 additions & 1 deletion homeway/homeway_linuxhost/sage/sagetranscribehandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,13 @@ async def HandleStreamingAudio(self, event: Event) -> bool:
text = await self.FiberManager.Listen(True, self.Buffer, SageDataTypesFormats.AudioPCM, self.IncomingAudioStartEvent.rate, self.IncomingAudioStartEvent.channels, self.IncomingAudioStartEvent.width)

# Check for a failure.
# Remember that an empty buffer isn't a failure, it means there were no words in the text!
if text is None:
await self._WriteError("Homeway Sage Listen - Audio Transcribe Failed")
return True

# Send the text back to the client.
self.Logger.debug(f"Sage Listen End - {text} - latency: {time.time() - start}s")
self.Logger.debug(f"Sage Listen End - `{text}` - latency: {time.time() - start}s")
await self._WriteEvent(Transcript(text=text).event())
return True

Expand Down

0 comments on commit 8c98a56

Please sign in to comment.