Documentation
¶
Index ¶
- Constants
- Variables
- func AudioFrame(stream int, samples []byte) ([]byte, error)
- func BytesPerFrame(sampleRate, milliseconds int) int
- func Encode(frame Frame) ([]byte, error)
- func SamplesOf(frame []byte) []byte
- func StreamOf(frame []byte) int
- type Agent
- type AgentClear
- type AgentSay
- type BaseHandler
- func (BaseHandler) OnAudio(context.Context, *Session, []byte)
- func (BaseHandler) OnConnected(context.Context, *ConnectionReady)
- func (BaseHandler) OnError(context.Context, *ErrorFrame)
- func (BaseHandler) OnFrame(context.Context, *Session, *UnknownFrame)
- func (BaseHandler) OnSessionEnd(context.Context, *Session, *SessionEnd)
- func (BaseHandler) OnSessionLost(context.Context, *Session)
- func (BaseHandler) OnSessionStart(context.Context, *Session)
- func (BaseHandler) OnToolResult(context.Context, *Session, *ToolResult)
- func (BaseHandler) OnUserSaid(context.Context, *Session, *UserSaid)
- type ConformanceHandler
- func (ConformanceHandler) OnAudio(_ context.Context, session *Session, samples []byte)
- func (ConformanceHandler) OnSessionStart(_ context.Context, session *Session)
- func (ConformanceHandler) OnToolResult(_ context.Context, session *Session, result *ToolResult)
- func (ConformanceHandler) OnUserSaid(ctx context.Context, session *Session, said *UserSaid)
- type ConformanceStep
- type ConnectionDraining
- type ConnectionReady
- type ErrorFrame
- type ErrorInfo
- type Frame
- type FrameError
- type Handler
- type Options
- type Party
- type Ping
- type Pong
- type Refused
- type Resume
- type SayOption
- type Session
- func (s *Session) Clear() error
- func (s *Session) End() *SessionEnd
- func (s *Session) Ended() bool
- func (s *Session) Hangup(ctx context.Context) (*ToolResult, error)
- func (s *Session) Hold(ctx context.Context, held bool) (*ToolResult, error)
- func (s *Session) Lost() bool
- func (s *Session) Received() int64
- func (s *Session) Resumable() bool
- func (s *Session) Say(text string, options ...SayOption) error
- func (s *Session) SendAudio(samples []byte) error
- func (s *Session) SendDTMF(ctx context.Context, digits string) (*ToolResult, error)
- func (s *Session) Sent() int64
- func (s *Session) Stream() (int, bool)
- func (s *Session) Token() (string, bool)
- func (s *Session) Tool(ctx context.Context, name string, arguments map[string]any) (*ToolResult, error)
- func (s *Session) Transfer(ctx context.Context, to *Party) (*ToolResult, error)
- func (s *Session) TransferNumber(ctx context.Context, number string) (*ToolResult, error)
- func (s *Session) WindowMs() int64
- type SessionEnd
- type SessionFrame
- type SessionResume
- type SessionResumed
- type SessionResumedAudio
- type SessionStart
- type SessionStartAudio
- type ToolCall
- type ToolResult
- type UnknownFrame
- type UserSaid
Constants ¶
const ConformanceGreeting = "Hello from the conformance agent."
ConformanceGreeting is what the conformance agent says first in a text session.
const ConnectPath = "/v1/agents/connect"
const DefaultURL = "wss://" + Host + ConnectPath
DefaultURL is where the agent connects when Options.URL is empty.
const HeaderBytes = 2
HeaderBytes is the size of the stream number before the samples.
const Host = "voice.sent.dm"
const ProtocolVersion = "0.2.0"
const RetainedFrames = 32
RetainedFrames is how many of its latest counted frames each side keeps per session, to send again on a resume.
const Subprotocol = "sent.voice.v1"
const Version = "0.1.0" // x-release-please-version
Version is this package's own version.
Variables ¶
var ErrSessionEnded = errors.New("the session has ended")
ErrSessionEnded is returned once the call has left the agent: session.end arrived, or the session was lost.
var ErrTextSession = errors.New("the session is a text session")
ErrTextSession is returned by SendAudio on a text session.
var FromAgent = map[string]struct{}{"agent.say": {}, "tool.call": {}, "agent.clear": {}, "session.resume": {}, "pong": {}}
var FromSent = map[string]struct{}{"connection.ready": {}, "connection.draining": {}, "ping": {}, "error": {}, "session.start": {}, "user.said": {}, "tool.result": {}, "session.end": {}, "session.resumed": {}}
FromSent is who sends what: a frame from the other side on the wrong direction is a protocol error.
var PartyKinds = []string{"user", "number", "conference", "agent", "anonymous"}
PartyKinds is the values Sent lists today; it may add more, so a runtime treats others as it would the fallback.
var SessionEndReasons = []string{"caller_hung_up", "agent_hung_up", "transferred", "max_duration", "agent_unreachable", "balance_exhausted", "voice_disabled", "failed"}
SessionEndReasons is the values Sent lists today; it may add more, so a runtime treats others as it would the fallback.
var SessionScoped = map[string]struct{}{"session.start": {}, "user.said": {}, "tool.result": {}, "session.end": {}, "session.resumed": {}, "agent.say": {}, "tool.call": {}, "agent.clear": {}, "session.resume": {}}
SessionScoped is the frames that belong to a session: they carry session_id.
var SessionTypes = map[string][]string{
"session.start": {"text", "audio"},
"user.said": {"text"},
"tool.result": {"text", "audio"},
"session.end": {"text", "audio"},
"session.resumed": {"text", "audio"},
"agent.say": {"text"},
"tool.call": {"text", "audio"},
"agent.clear": {"text", "audio"},
"session.resume": {"text", "audio"},
}
SessionTypes is which session types allow each session frame.
var Uncounted = map[string]struct{}{"error": {}, "session.start": {}, "session.resumed": {}, "session.resume": {}, "audio": {}}
Uncounted is the frames that do not count towards `received`, whatever they carry; every other frame with a session_id counts, also one of an unknown type.
Functions ¶
func AudioFrame ¶
AudioFrame is a frame carrying samples for stream.
func BytesPerFrame ¶
BytesPerFrame is how many bytes of samples carry milliseconds of audio at sampleRate: 640 for 20 ms at 16000 Hz.
func Encode ¶
Encode is the JSON to send for a frame, type first. It returns a *FrameError when the frame breaks the protocol.
Types ¶
type Agent ¶
type Agent struct {
// contains filtered or unexported fields
}
Agent serves calls. Its Handler's methods run on the session's own goroutine, in order.
func (*Agent) Run ¶
Run serves calls until Stop is called or ctx ends. It returns a *Refused when Sent refuses the connection for a reason that needs a person, and nil otherwise.
type AgentClear ¶
type AgentClear struct {
// SessionID: The session: one call while one agent is on it.
SessionID string `json:"session_id"`
}
AgentClear: Drop the speech or audio the agent sent that Sent has not played yet.
func (AgentClear) MarshalJSON ¶
func (f AgentClear) MarshalJSON() ([]byte, error)
func (*AgentClear) Session ¶
func (f *AgentClear) Session() string
func (*AgentClear) Type ¶
func (*AgentClear) Type() string
Type is the wire type of AgentClear: agent.clear.
type AgentSay ¶
type AgentSay struct {
// SessionID: The session: one call while one agent is on it.
SessionID string `json:"session_id"`
Text string `json:"text"`
// Interruptible: Whether the caller speaking stops this speech.
Interruptible *bool `json:"interruptible,omitempty"`
// Last: false when more text for the same utterance follows.
Last *bool `json:"last,omitempty"`
}
AgentSay: Text sessions: speak this to the caller.
func (AgentSay) MarshalJSON ¶
type BaseHandler ¶
type BaseHandler struct{}
BaseHandler does nothing on every event. Embed it and override the methods you need.
func (BaseHandler) OnConnected ¶
func (BaseHandler) OnConnected(context.Context, *ConnectionReady)
func (BaseHandler) OnError ¶
func (BaseHandler) OnError(context.Context, *ErrorFrame)
func (BaseHandler) OnFrame ¶
func (BaseHandler) OnFrame(context.Context, *Session, *UnknownFrame)
func (BaseHandler) OnSessionEnd ¶
func (BaseHandler) OnSessionEnd(context.Context, *Session, *SessionEnd)
func (BaseHandler) OnSessionLost ¶
func (BaseHandler) OnSessionLost(context.Context, *Session)
func (BaseHandler) OnSessionStart ¶
func (BaseHandler) OnSessionStart(context.Context, *Session)
func (BaseHandler) OnToolResult ¶
func (BaseHandler) OnToolResult(context.Context, *Session, *ToolResult)
func (BaseHandler) OnUserSaid ¶
func (BaseHandler) OnUserSaid(context.Context, *Session, *UserSaid)
type ConformanceHandler ¶
type ConformanceHandler struct{ BaseHandler }
ConformanceHandler is the conformance agent's behaviour.
func (ConformanceHandler) OnAudio ¶
func (ConformanceHandler) OnAudio(_ context.Context, session *Session, samples []byte)
func (ConformanceHandler) OnSessionStart ¶
func (ConformanceHandler) OnSessionStart(_ context.Context, session *Session)
func (ConformanceHandler) OnToolResult ¶
func (ConformanceHandler) OnToolResult(_ context.Context, session *Session, result *ToolResult)
func (ConformanceHandler) OnUserSaid ¶
func (ConformanceHandler) OnUserSaid(ctx context.Context, session *Session, said *UserSaid)
type ConformanceStep ¶
type ConformanceStep struct {
// Action is say or tool.
Action string
Text string
Last *bool
Name string
Arguments map[string]any
}
ConformanceStep is one action the conformance agent performs for a user.said.
func Decide ¶
func Decide(text string) []ConformanceStep
Decide is what a user.said asks for: the actions the agent performs in order. A command is the text up to the first space; stream takes the next word as the first piece.
type ConnectionDraining ¶
type ConnectionDraining struct {
}
ConnectionDraining: No new sessions will start on this connection. Open a new one now, while sessions already on this one go on.
func (ConnectionDraining) MarshalJSON ¶
func (f ConnectionDraining) MarshalJSON() ([]byte, error)
func (*ConnectionDraining) Type ¶
func (*ConnectionDraining) Type() string
Type is the wire type of ConnectionDraining: connection.draining.
type ConnectionReady ¶
type ConnectionReady struct {
// ProtocolVersion: The version of this protocol Sent speaks on the connection.
ProtocolVersion string `json:"protocol_version"`
// HeartbeatIntervalMs: How often Sent sends ping on the connection.
HeartbeatIntervalMs int64 `json:"heartbeat_interval_ms"`
// MaxSessions: The most sessions Sent puts on the connection at once.
MaxSessions int64 `json:"max_sessions"`
}
ConnectionReady: First on a connection the agent opened: it is authenticated, and sessions will follow.
func (ConnectionReady) MarshalJSON ¶
func (f ConnectionReady) MarshalJSON() ([]byte, error)
func (*ConnectionReady) Type ¶
func (*ConnectionReady) Type() string
Type is the wire type of ConnectionReady: connection.ready.
type ErrorFrame ¶
type ErrorFrame struct {
Error *ErrorInfo `json:"error"`
// SessionID: The session: one call while one agent is on it.
SessionID *string `json:"session_id,omitempty"`
// FrameType: The type of the frame that was not accepted, when it had one.
FrameType *string `json:"frame_type,omitempty"`
}
ErrorFrame: A frame the agent sent was not accepted and had no effect. The connection stays open.
func (ErrorFrame) MarshalJSON ¶
func (f ErrorFrame) MarshalJSON() ([]byte, error)
func (*ErrorFrame) Type ¶
func (*ErrorFrame) Type() string
Type is the wire type of ErrorFrame: error.
type ErrorInfo ¶
type ErrorInfo struct {
// Code: Stable and machine-readable. Sent may add codes.
Code string `json:"code"`
// Message: For people. It may change without notice.
Message string `json:"message"`
}
ErrorInfo is the ErrorInfo object.
type Frame ¶
type Frame interface {
Type() string
}
Frame is one frame of the protocol; Type is its wire `type`.
type FrameError ¶
type FrameError struct {
Message string
}
FrameError is a frame that does not fit the protocol.
func (*FrameError) Error ¶
func (e *FrameError) Error() string
type Handler ¶
type Handler interface {
// OnConnected: a connection to Sent is ready for sessions.
OnConnected(ctx context.Context, ready *ConnectionReady)
// OnSessionStart: a call reached the agent. In a text session, Say to speak; an audio session waits for audio.
OnSessionStart(ctx context.Context, session *Session)
// OnUserSaid: text sessions, the caller said something. With Correction true it replaces the previous one.
OnUserSaid(ctx context.Context, session *Session, said *UserSaid)
// OnAudio: audio sessions, the caller's audio, 16-bit little-endian mono PCM at session.SampleRate.
OnAudio(ctx context.Context, session *Session, samples []byte)
// OnToolResult: Sent answered a tool call. The call's waiting Tool gets the same result.
OnToolResult(ctx context.Context, session *Session, result *ToolResult)
// OnFrame: a session frame this SDK version does not know. It was counted; nothing else is needed.
OnFrame(ctx context.Context, session *Session, frame *UnknownFrame)
// OnSessionEnd: the call left the agent. end.Reason says why.
OnSessionEnd(ctx context.Context, session *Session, end *SessionEnd)
// OnSessionLost: the session's connection was lost and it could not be resumed. Sent ends the call with
// agent_unreachable; no session.end follows.
OnSessionLost(ctx context.Context, session *Session)
// OnError: Sent refused a frame; the connection stays open.
OnError(ctx context.Context, err *ErrorFrame)
}
Handler is what an agent does on its calls. Embed BaseHandler and override what you need.
type Options ¶
type Options struct {
// APIKey is the account API key. Keep it on the server the agent runs on.
APIKey string
// AgentID is the agent, as POST /v3/voice/agents created it.
AgentID string
// URL is where to connect. Empty means Sent.
URL string
// Handler is what the agent does on its calls.
Handler Handler
// Logger is where the runtime's log lines go. Nil means slog's default; tokens never appear in them.
Logger *slog.Logger
}
Options is what an agent needs.
type Party ¶
type Party struct {
// Kind: user, number, conference, agent or anonymous. Sent may add kinds.
Kind string `json:"kind"`
// Value: The app user's identity, the E.164 number, the room name or the agent id. Absent for anonymous.
Value *string `json:"value,omitempty"`
}
Party: One end of a call, in the shape of the REST API's call parties.
type Ping ¶
type Ping struct {
// Ts: Milliseconds since the Unix epoch.
Ts int64 `json:"ts"`
}
Ping: Sent's heartbeat on a connection. Answer with pong carrying the same ts.
func (Ping) MarshalJSON ¶
type Pong ¶
type Pong struct {
// Ts: Milliseconds since the Unix epoch.
Ts int64 `json:"ts"`
}
Pong: The answer to ping, with the same ts.
func (Pong) MarshalJSON ¶
type Refused ¶
Refused is the error Run returns when Sent refused the connection for a reason that needs a person.
type Resume ¶
type Resume struct {
// Token: A secret that moves one session to a new connection, once. Opaque: keep it out of URLs and logs.
Token string `json:"token"`
// WindowMs: How long Sent keeps the session for a resume after its connection is lost, or after it ends.
WindowMs int64 `json:"window_ms"`
}
Resume: How to resume the session on a new connection. See Resuming sessions in docs/protocol.md.
type SayOption ¶
type SayOption func(*AgentSay)
SayOption changes how a Say is spoken.
func Interruptible ¶
Interruptible is whether the caller speaking stops this speech.
type Session ¶
type Session struct {
ID string
CallID string
AgentID string
Direction string
From *Party
To *Party
// SessionType is text or audio.
SessionType string
Language string
Context map[string]any
StartedAt string
// SampleRate is the Hz of the session's 16-bit mono PCM, both ways, in an audio session.
SampleRate int
// Start is what session.start said about the call.
Start *SessionStart
// contains filtered or unexported fields
}
Session is the call the agent is on, and the ways to act on it. The agent creates it; its handlers run in order.
func (*Session) Clear ¶
Clear drops the speech or audio the agent sent that Sent has not played yet.
func (*Session) Ended ¶
Ended is whether the call has left the agent: session.end arrived, or the session was lost.
func (*Session) Hangup ¶
func (s *Session) Hangup(ctx context.Context) (*ToolResult, error)
Hangup ends the call, after the speech the agent queued before it.
func (*Session) Lost ¶
Lost is whether the connection was lost and the session could not be resumed: Sent ends the call without a session.end.
func (*Session) Received ¶
Received is how many of Sent's counted frames for the session the agent has read: the received of a resume.
func (*Session) Resumable ¶
Resumable is whether the session survives a lost connection: it has a resume token.
func (*Session) SendAudio ¶
SendAudio plays samples, 16-bit little-endian mono PCM at the session's sample rate, in an audio session. Send 20 ms per call, and no more than 200 ms. It returns ErrSessionEnded once the call has left the agent.
func (*Session) SendDTMF ¶
SendDTMF plays digits on the call: 0 to 9, *, #, A to D, and w to wait half a second.
func (*Session) Stream ¶
Stream is the number in the header of the session's binary audio frames on its current connection.
func (*Session) Token ¶
Token is the latest resume token, when the session can be resumed. Keep it out of logs.
func (*Session) Tool ¶
func (s *Session) Tool(ctx context.Context, name string, arguments map[string]any) (*ToolResult, error)
Tool calls a tool by name and waits for its tool.result. It returns ErrSessionEnded if the session ends first, and ctx's error if ctx ends first.
func (*Session) Transfer ¶
Transfer moves the call to another party. On success the session then ends with transferred.
func (*Session) TransferNumber ¶
TransferNumber moves the call to an E.164 number.
type SessionEnd ¶
type SessionEnd struct {
// SessionID: The session: one call while one agent is on it.
SessionID string `json:"session_id"`
// Reason: Why the call left the agent. Sent may add reasons, so treat one you do not know as failed.
Reason string `json:"reason"`
DurationMs int64 `json:"duration_ms"`
}
SessionEnd: Last for every session: the call has left the agent.
func (SessionEnd) MarshalJSON ¶
func (f SessionEnd) MarshalJSON() ([]byte, error)
func (*SessionEnd) Session ¶
func (f *SessionEnd) Session() string
func (*SessionEnd) Type ¶
func (*SessionEnd) Type() string
Type is the wire type of SessionEnd: session.end.
type SessionFrame ¶
SessionFrame is a frame that belongs to a session: it carries session_id.
type SessionResume ¶
type SessionResume struct {
// SessionID: The session: one call while one agent is on it.
SessionID string `json:"session_id"`
// Token: A secret that moves one session to a new connection, once. Opaque: keep it out of URLs and logs.
Token string `json:"token"`
// Received: How many of the other side's frames for the session were read. Resuming sessions in docs/protocol.md says which frames count.
Received int64 `json:"received"`
}
SessionResume: Move a session to this connection, after its connection was lost or while it drains.
func (SessionResume) MarshalJSON ¶
func (f SessionResume) MarshalJSON() ([]byte, error)
func (*SessionResume) Session ¶
func (f *SessionResume) Session() string
func (*SessionResume) Type ¶
func (*SessionResume) Type() string
Type is the wire type of SessionResume: session.resume.
type SessionResumed ¶
type SessionResumed struct {
// SessionID: The session: one call while one agent is on it.
SessionID string `json:"session_id"`
// Received: How many of the other side's frames for the session were read. Resuming sessions in docs/protocol.md says which frames count.
Received int64 `json:"received"`
// Resume: How to resume the session on a new connection. See Resuming sessions in docs/protocol.md.
Resume *Resume `json:"resume,omitempty"`
// Audio: Present on an audio session that is still live: its stream on this connection, which can differ from the one before. Its sample rate does not change.
Audio *SessionResumedAudio `json:"audio,omitempty"`
}
SessionResumed: The session is on this connection now. Sent's frames the agent did not read follow, in order.
func (SessionResumed) MarshalJSON ¶
func (f SessionResumed) MarshalJSON() ([]byte, error)
func (*SessionResumed) Session ¶
func (f *SessionResumed) Session() string
func (*SessionResumed) Type ¶
func (*SessionResumed) Type() string
Type is the wire type of SessionResumed: session.resumed.
type SessionResumedAudio ¶
type SessionResumedAudio struct {
// Stream: The number in the header of the session's binary audio frames, unique among the sessions open on the connection.
Stream int64 `json:"stream"`
}
SessionResumedAudio: Present on an audio session that is still live: its stream on this connection, which can differ from the one before. Its sample rate does not change.
type SessionStart ¶
type SessionStart struct {
// SessionID: The session: one call while one agent is on it.
SessionID string `json:"session_id"`
// CallID: The call, as the REST API's /v3/voice/calls knows it. It stays the same when the call moves to a person.
CallID string `json:"call_id"`
// AgentID: The agent, as the REST API's /v3/voice/agents knows it.
AgentID string `json:"agent_id"`
Direction string `json:"direction"`
// From: One end of a call, in the shape of the REST API's call parties.
From *Party `json:"from"`
// To: One end of a call, in the shape of the REST API's call parties.
To *Party `json:"to"`
SessionType string `json:"session_type"`
// Language: The caller's language as a BCP 47 tag, from the agent's speech settings.
Language *string `json:"language,omitempty"`
// Audio: Present on audio sessions.
Audio *SessionStartAudio `json:"audio,omitempty"`
// MaxDurationMs: When Sent ends the session with reason max_duration, counted from started_at.
MaxDurationMs *int64 `json:"max_duration_ms,omitempty"`
// Context: What the customer's backend passed when it started an outbound call. At most 4 KB as JSON.
Context map[string]any `json:"context,omitempty"`
// Resume: How to resume the session on a new connection. See Resuming sessions in docs/protocol.md.
Resume *Resume `json:"resume,omitempty"`
// StartedAt: When the session started, in ISO 8601, as the REST API writes times.
StartedAt string `json:"started_at"`
}
SessionStart: First for every session: a call has reached the agent.
func (SessionStart) MarshalJSON ¶
func (f SessionStart) MarshalJSON() ([]byte, error)
func (*SessionStart) Session ¶
func (f *SessionStart) Session() string
func (*SessionStart) Type ¶
func (*SessionStart) Type() string
Type is the wire type of SessionStart: session.start.
type SessionStartAudio ¶
type SessionStartAudio struct {
// Stream: The number in the header of the session's binary audio frames, unique among the sessions open on the connection.
Stream int64 `json:"stream"`
// SampleRate: Hz of the session's 16-bit mono PCM, both ways.
SampleRate *int64 `json:"sample_rate,omitempty"`
}
SessionStartAudio: Present on audio sessions.
type ToolCall ¶
type ToolCall struct {
// SessionID: The session: one call while one agent is on it.
SessionID string `json:"session_id"`
// CallRef: Chosen by the agent for a tool.call, unique within the session, and repeated in its tool.result.
CallRef string `json:"call_ref"`
Name string `json:"name"`
Arguments map[string]any `json:"arguments,omitempty"`
}
ToolCall: Act on the call with a built-in tool. Sent answers with exactly one tool.result.
func (ToolCall) MarshalJSON ¶
type ToolResult ¶
type ToolResult struct {
// SessionID: The session: one call while one agent is on it.
SessionID string `json:"session_id"`
// CallRef: Chosen by the agent for a tool.call, unique within the session, and repeated in its tool.result.
CallRef string `json:"call_ref"`
Ok bool `json:"ok"`
Error *ErrorInfo `json:"error,omitempty"`
}
ToolResult: The outcome of one tool.call, with its call_ref.
func (ToolResult) MarshalJSON ¶
func (f ToolResult) MarshalJSON() ([]byte, error)
func (*ToolResult) Session ¶
func (f *ToolResult) Session() string
func (*ToolResult) Type ¶
func (*ToolResult) Type() string
Type is the wire type of ToolResult: tool.result.
type UnknownFrame ¶
type UnknownFrame struct {
// SessionID is the session the frame names, or empty.
SessionID string
// Fields is the frame as it arrived, every field included.
Fields map[string]any
// contains filtered or unexported fields
}
UnknownFrame is a frame of a type this version does not know. Ignore it; count it when it names a session.
func NewUnknownFrame ¶
func NewUnknownFrame(fields map[string]any) *UnknownFrame
NewUnknownFrame is a frame of a type this version does not know, as its fields, type included.
func (UnknownFrame) MarshalJSON ¶
func (f UnknownFrame) MarshalJSON() ([]byte, error)
func (*UnknownFrame) Session ¶
func (f *UnknownFrame) Session() string
func (*UnknownFrame) Type ¶
func (f *UnknownFrame) Type() string
type UserSaid ¶
type UserSaid struct {
// SessionID: The session: one call while one agent is on it.
SessionID string `json:"session_id"`
Text string `json:"text"`
// Correction: true when this replaces the session's previous user.said.
Correction bool `json:"correction"`
}
UserSaid: Text sessions: one complete utterance from the caller.
func (UserSaid) MarshalJSON ¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
conformance
command
The conformance agent of sent-dm-voice-protocol, as a program: the protocol's harness runs it with SENT_CONFORMANCE_URL set, in CI before every release, and by hand against a harness on localhost.
|
The conformance agent of sent-dm-voice-protocol, as a program: the protocol's harness runs it with SENT_CONFORMANCE_URL set, in CI before every release, and by hand against a harness on localhost. |