pinecall 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +167 -0
  3. data/LICENSE +202 -0
  4. data/README.md +140 -0
  5. data/lib/pinecall/agent/author.rb +31 -0
  6. data/lib/pinecall/agent/config.rb +124 -0
  7. data/lib/pinecall/agent/doc.rb +73 -0
  8. data/lib/pinecall/agent/knowledge.rb +18 -0
  9. data/lib/pinecall/agent/searching.rb +33 -0
  10. data/lib/pinecall/agent/spec.rb +133 -0
  11. data/lib/pinecall/agent/state.rb +185 -0
  12. data/lib/pinecall/agent/tools.rb +119 -0
  13. data/lib/pinecall/agent.rb +160 -0
  14. data/lib/pinecall/blocks.rb +152 -0
  15. data/lib/pinecall/bridge.rb +239 -0
  16. data/lib/pinecall/call_world/answers.rb +61 -0
  17. data/lib/pinecall/call_world/room.rb +25 -0
  18. data/lib/pinecall/call_world.rb +175 -0
  19. data/lib/pinecall/client/agent.rb +179 -0
  20. data/lib/pinecall/client/call.rb +140 -0
  21. data/lib/pinecall/client/connection.rb +190 -0
  22. data/lib/pinecall/client/endpoints.rb +30 -0
  23. data/lib/pinecall/client/listeners.rb +42 -0
  24. data/lib/pinecall/client/observe.rb +109 -0
  25. data/lib/pinecall/client/rest.rb +61 -0
  26. data/lib/pinecall/client.rb +212 -0
  27. data/lib/pinecall/errors.rb +53 -0
  28. data/lib/pinecall/panel.rb +138 -0
  29. data/lib/pinecall/reading.rb +37 -0
  30. data/lib/pinecall/rules.rb +38 -0
  31. data/lib/pinecall/serve/held.rb +40 -0
  32. data/lib/pinecall/serve/loading.rb +102 -0
  33. data/lib/pinecall/serve/viewing.rb +40 -0
  34. data/lib/pinecall/serve.rb +169 -0
  35. data/lib/pinecall/testing.rb +190 -0
  36. data/lib/pinecall/version.rb +6 -0
  37. data/lib/pinecall/view.rb +70 -0
  38. data/lib/pinecall/wire/codec.rb +75 -0
  39. data/lib/pinecall/wire/enums.rb +104 -0
  40. data/lib/pinecall/wire/errors.rb +12 -0
  41. data/lib/pinecall/wire/reduce.rb +249 -0
  42. data/lib/pinecall/wire/registry.rb +168 -0
  43. data/lib/pinecall/wire/shapes.rb +33 -0
  44. data/lib/pinecall/wire/shapes_call_events.rb +137 -0
  45. data/lib/pinecall/wire/shapes_commands.rb +114 -0
  46. data/lib/pinecall/wire/shapes_config.rb +70 -0
  47. data/lib/pinecall/wire/shapes_doors.rb +96 -0
  48. data/lib/pinecall/wire/shapes_events.rb +358 -0
  49. data/lib/pinecall/wire/shapes_metrics.rb +109 -0
  50. data/lib/pinecall/wire/shapes_parts.rb +340 -0
  51. data/lib/pinecall/wire/state.rb +56 -0
  52. data/lib/pinecall/wire/validate.rb +154 -0
  53. data/lib/pinecall/wire.rb +43 -0
  54. data/lib/pinecall.rb +45 -0
  55. data/sig/pinecall.rbs +453 -0
  56. metadata +119 -0
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The shape of each event's data in a call's life and in the agent that holds it.
4
+
5
+ module Pinecall
6
+ module Wire
7
+ module Shapes
8
+ CALL_EVENTS = {
9
+ "AgentConfigured" => {
10
+ changed: { kind: :list, items: { kind: :str }, required: true }
11
+ }.freeze,
12
+ "AgentDetached" => {
13
+ app: { kind: :str, required: true },
14
+ env: { kind: :ref, ref: "Env", required: true },
15
+ left: { kind: :bool, required: true }
16
+ }.freeze,
17
+ "AgentDraining" => {
18
+ app: { kind: :str, required: true },
19
+ env: { kind: :ref, ref: "Env", required: true },
20
+ handed: { kind: :int, required: true },
21
+ parked: { kind: :int, required: true }
22
+ }.freeze,
23
+ "AgentRegistered" => {
24
+ routes: { kind: :list, items: { kind: :ref, ref: "Route" }, required: true },
25
+ app: { kind: :str, required: true },
26
+ sdk: { kind: :str },
27
+ env: { kind: :ref, ref: "Env" }
28
+ }.freeze,
29
+ "AgentStateChanged" => {
30
+ state: { kind: :ref, ref: "AgentState", required: true }
31
+ }.freeze,
32
+ "AgentTranscript" => {
33
+ speech_id: { kind: :str, required: true },
34
+ text: { kind: :str, required: true },
35
+ final: { kind: :bool, required: true },
36
+ start: { kind: :float },
37
+ end: { kind: :float }
38
+ }.freeze,
39
+ "CallAttached" => {
40
+ app: { kind: :str, required: true },
41
+ started: { kind: :ref, ref: "CallStarted", required: true },
42
+ state: { kind: :json, required: true },
43
+ seq: { kind: :int, required: true },
44
+ claimed: { kind: :str, null: true }
45
+ }.freeze,
46
+ "CallClaimed" => {
47
+ code: { kind: :str, required: true },
48
+ via: { kind: :enum, values: %w[keypad agent], required: true }
49
+ }.freeze,
50
+ "CallDialing" => {
51
+ channel: { kind: :ref, ref: "Channel", required: true },
52
+ from: { kind: :str, required: true },
53
+ to: { kind: :str, required: true },
54
+ run: { kind: :str, null: true },
55
+ caller: { kind: :ref, null: true, ref: "Contact", required: true },
56
+ external_id: { kind: :str },
57
+ asked_by: { kind: :str }
58
+ }.freeze,
59
+ "CallEnded" => {
60
+ reason: { kind: :ref, ref: "EndReason", required: true },
61
+ ended_by: { kind: :ref, ref: "EndedBy", required: true },
62
+ ended_at: { kind: :float, required: true },
63
+ duration_s: { kind: :float, required: true }
64
+ }.freeze,
65
+ "CallLine" => {
66
+ held: { kind: :bool, required: true },
67
+ muted: { kind: :bool, required: true }
68
+ }.freeze,
69
+ "CallRinging" => {
70
+ channel: { kind: :ref, ref: "Channel", required: true },
71
+ from: { kind: :str, required: true },
72
+ to: { kind: :str, required: true },
73
+ route: { kind: :ref, ref: "Route", required: true },
74
+ run: { kind: :str, null: true },
75
+ caller: { kind: :ref, null: true, ref: "Contact", required: true },
76
+ external_id: { kind: :str }
77
+ }.freeze,
78
+ "CallScore" => {
79
+ passed: { kind: :bool },
80
+ not_judged: { kind: :str },
81
+ judges: { kind: :list, items: { kind: :ref, ref: "Judgment" }, required: true },
82
+ panel: { kind: :list, items: { kind: :str } },
83
+ judge_calls: { kind: :int, required: true },
84
+ judge_cost_usd: { kind: :float },
85
+ judged_by: { kind: :ref, ref: "JudgedBy" }
86
+ }.freeze,
87
+ "CallStarted" => {
88
+ channel: { kind: :ref, ref: "Channel", required: true },
89
+ direction: { kind: :ref, ref: "Direction", required: true },
90
+ from: { kind: :str, required: true },
91
+ to: { kind: :str, required: true },
92
+ run: { kind: :str, null: true },
93
+ persona: { kind: :str, null: true },
94
+ accepts_when: { kind: :str, null: true },
95
+ declines_when: { kind: :str, null: true },
96
+ caller: { kind: :ref, null: true, ref: "Contact", required: true },
97
+ started_at: { kind: :float, required: true },
98
+ env: { kind: :ref, ref: "Env" },
99
+ worker: { kind: :str, null: true },
100
+ medium: { kind: :ref, ref: "Medium", null: true },
101
+ state: { kind: :json }
102
+ }.freeze,
103
+ "CallSummary" => {
104
+ reason: { kind: :ref, ref: "EndReason", required: true },
105
+ outcome: { kind: :str, required: true },
106
+ duration_s: { kind: :float, required: true },
107
+ turns: { kind: :int, required: true },
108
+ usage: { kind: :list, items: { kind: :ref, ref: "ModelUsage" }, required: true },
109
+ cost: { kind: :ref, ref: "Cost", required: true },
110
+ recording: { kind: :str }
111
+ }.freeze,
112
+ "CallTransferred" => {
113
+ to: { kind: :str, required: true },
114
+ mode: { kind: :ref, null: true, ref: "TransferMode" },
115
+ ok: { kind: :bool, required: true },
116
+ error: { kind: :str }
117
+ }.freeze,
118
+ "CallbackRequested" => {
119
+ channel: { kind: :ref, ref: "Channel", required: true },
120
+ number: { kind: :str, required: true },
121
+ via: { kind: :enum, values: %w[overflow widget agent], required: true },
122
+ call: { kind: :str, null: true, required: true },
123
+ when: { kind: :str },
124
+ note: { kind: :str },
125
+ contact: { kind: :ref, null: true, ref: "Contact", required: true }
126
+ }.freeze,
127
+ "AgentTurnEnded" => {
128
+ speech_id: { kind: :str, required: true },
129
+ item_id: { kind: :str },
130
+ text: { kind: :str, required: true },
131
+ interrupted: { kind: :bool, required: true },
132
+ metrics: { kind: :ref, ref: "AgentTurnMetrics", required: true }
133
+ }.freeze,
134
+ }.freeze
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The shape of each command's data, as an app sends it.
4
+
5
+ module Pinecall
6
+ module Wire
7
+ module Shapes
8
+ COMMANDS = {
9
+ "AgentConfigure" => {
10
+ config: { kind: :ref, ref: "AgentConfig", required: true }
11
+ }.freeze,
12
+ "AgentDrain" => {}.freeze,
13
+ "AgentRegister" => {
14
+ routes: { kind: :list, items: { kind: :ref, ref: "Route" }, required: true },
15
+ sdk: { kind: :str },
16
+ host: { kind: :str },
17
+ takes_unclaimed: { kind: :bool, default: true },
18
+ answers_dev: { kind: :bool, default: false }
19
+ }.freeze,
20
+ "AgentReply" => {
21
+ instructions: { kind: :str, required: true },
22
+ allow_interruptions: { kind: :bool }
23
+ }.freeze,
24
+ "AgentSay" => {
25
+ text: { kind: :str, required: true },
26
+ allow_interruptions: { kind: :bool }
27
+ }.freeze,
28
+ "CallAttention" => {
29
+ reason: { kind: :str, required: true },
30
+ wait_s: { kind: :float, required: true }
31
+ }.freeze,
32
+ "CallCallback" => {
33
+ number: { kind: :str, required: true },
34
+ when: { kind: :str },
35
+ note: { kind: :str }
36
+ }.freeze,
37
+ "CallClaim" => {
38
+ code: { kind: :str, required: true, pattern: "^[0-9]{4}$" }
39
+ }.freeze,
40
+ "CallDial" => {
41
+ to: { kind: :str, required: true },
42
+ from: { kind: :str },
43
+ caller: { kind: :ref, ref: "Contact" },
44
+ metadata: { kind: :json }
45
+ }.freeze,
46
+ "CallDtmf" => {
47
+ digits: { kind: :str, required: true }
48
+ }.freeze,
49
+ "CallEvent" => {
50
+ name: { kind: :str, required: true },
51
+ data: { kind: :json, required: true }
52
+ }.freeze,
53
+ "CallHangup" => {
54
+ reason: { kind: :str }
55
+ }.freeze,
56
+ "CallHold" => {}.freeze,
57
+ "CallOptOut" => {
58
+ note: { kind: :str, null: true }
59
+ }.freeze,
60
+ "CallLog" => {
61
+ name: { kind: :str, required: true },
62
+ data: { kind: :json, required: true }
63
+ }.freeze,
64
+ "CallMute" => {}.freeze,
65
+ "CallTransfer" => {
66
+ to: { kind: :str, required: true },
67
+ mode: { kind: :ref, ref: "TransferMode" }
68
+ }.freeze,
69
+ "CallUnhold" => {}.freeze,
70
+ "CallUnmute" => {}.freeze,
71
+ "DevAnswer" => {
72
+ id: { kind: :str, required: true },
73
+ result: { kind: :json },
74
+ refused: { kind: :ref, ref: "DevRefusal" }
75
+ }.freeze,
76
+ "ParticipantMute" => {
77
+ identity: { kind: :str, required: true }
78
+ }.freeze,
79
+ "ParticipantRemove" => {
80
+ identity: { kind: :str, required: true }
81
+ }.freeze,
82
+ "Ping" => {}.freeze,
83
+ "PromptSet" => {
84
+ name: { kind: :str, required: true },
85
+ text: { kind: :str, required: true }
86
+ }.freeze,
87
+ "RoomInvite" => {
88
+ to: { kind: :str, required: true },
89
+ kind: { kind: :enum, values: %w[sip participant], required: true }
90
+ }.freeze,
91
+ "RoomSend" => {
92
+ topic: { kind: :str, required: true },
93
+ data: { kind: :json, required: true },
94
+ to: { kind: :str }
95
+ }.freeze,
96
+ "SessionConfigure" => {
97
+ state: { kind: :json },
98
+ config: { kind: :ref, ref: "AgentConfig" }
99
+ }.freeze,
100
+ "StateSet" => {
101
+ state: { kind: :json, required: true },
102
+ changed: { kind: :list, items: { kind: :str } }
103
+ }.freeze,
104
+ "SupervisorVerb" => {
105
+ by: { kind: :ref, ref: "Supervisor", required: true },
106
+ verb: { kind: :ref, ref: "Verb", required: true }
107
+ }.freeze,
108
+ "ToolsSet" => {
109
+ tools: { kind: :list, items: { kind: :ref, ref: "ToolSpec" }, required: true }
110
+ }.freeze,
111
+ }.freeze
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ # An agent as its app declares it: voice, models, turns, tools, views, docs, memory.
4
+
5
+ module Pinecall
6
+ module Wire
7
+ module Shapes
8
+ CONFIG = {
9
+ "PromptBlockSpec" => {
10
+ name: { kind: :str, required: true, pattern: "^[a-z][a-z0-9_]*$" },
11
+ region: { kind: :ref, ref: "PromptRegion", required: true }
12
+ }.freeze,
13
+ "VoiceConfig" => {
14
+ name: { kind: :str },
15
+ provider: { kind: :str },
16
+ model: { kind: :str },
17
+ voice_id: { kind: :str }
18
+ }.freeze,
19
+ "ModelConfig" => {
20
+ provider: { kind: :str, required: true },
21
+ model: { kind: :str, required: true },
22
+ temperature: { kind: :float }
23
+ }.freeze,
24
+ "TurnConfig" => {
25
+ min_interruption_words: { kind: :int },
26
+ endpointing_ms: { kind: :int },
27
+ eot_threshold: { kind: :float },
28
+ eager_eot_threshold: { kind: :float }
29
+ }.freeze,
30
+ "Pronunciation" => {
31
+ word: { kind: :str, required: true },
32
+ spoken: { kind: :str, required: true }
33
+ }.freeze,
34
+ "StateFieldSpec" => {
35
+ name: { kind: :str, required: true },
36
+ visibility: { kind: :ref, ref: "Visibility", required: true }
37
+ }.freeze,
38
+ "ViewSpec" => {
39
+ name: { kind: :str, required: true }
40
+ }.freeze,
41
+ "EventSpec" => {
42
+ name: { kind: :str, required: true },
43
+ from: { kind: :list, items: { kind: :ref, ref: "EventSource" }, required: true }
44
+ }.freeze,
45
+ "KnowledgeFile" => {
46
+ path: { kind: :str, required: true },
47
+ text: { kind: :str, required: true }
48
+ }.freeze,
49
+ "DocsConfig" => {
50
+ base: { kind: :str, required: true },
51
+ mode: { kind: :ref, ref: "DocsMode", default: "retrieved" },
52
+ k: { kind: :int, default: 8 },
53
+ min_score: { kind: :float }
54
+ }.freeze,
55
+ "GreetingConfig" => {
56
+ say: { kind: :str },
57
+ reply: { kind: :str },
58
+ allow_interruptions: { kind: :bool }
59
+ }.freeze,
60
+ "HangupConfig" => {
61
+ when: { kind: :str, default: "" }
62
+ }.freeze,
63
+ "MemoryConfig" => {
64
+ remember: { kind: :list, items: { kind: :str }, default: [] },
65
+ forget: { kind: :list, items: { kind: :str }, default: [] }
66
+ }.freeze,
67
+ }.freeze
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The bodies and answers of the doors this gem calls: knowledge, memory, the agent's config.
4
+
5
+ module Pinecall
6
+ module Wire
7
+ module Shapes
8
+ DOORS = {
9
+ "ToolSpec" => {
10
+ name: { kind: :str, required: true },
11
+ description: { kind: :str, required: true },
12
+ parameters: { kind: :json, required: true },
13
+ side_effect: { kind: :enum, values: %w[read write irreversible], default: "read" },
14
+ confirm: { kind: :str },
15
+ pii: { kind: :list, items: { kind: :str } },
16
+ timeout_s: { kind: :float }
17
+ }.freeze,
18
+ "AgentConfig" => {
19
+ prompt: { kind: :list, items: { kind: :ref, ref: "PromptBlockSpec" } },
20
+ greeting: { kind: :ref, ref: "GreetingConfig" },
21
+ voice: { kind: :ref, ref: "VoiceConfig" },
22
+ llm: { kind: :ref, ref: "ModelConfig" },
23
+ stt: { kind: :ref, ref: "ModelConfig" },
24
+ turn: { kind: :ref, ref: "TurnConfig" },
25
+ says: { kind: :list, items: { kind: :ref, ref: "Pronunciation" } },
26
+ hears: { kind: :list, items: { kind: :str } },
27
+ knowledge: { kind: :ref, ref: "KnowledgeFile" },
28
+ docs: { kind: :ref, ref: "DocsConfig" },
29
+ memory: { kind: :ref, ref: "MemoryConfig" },
30
+ hangup: { kind: :ref, ref: "HangupConfig" },
31
+ record: { kind: :bool, default: true },
32
+ tools: { kind: :list, items: { kind: :ref, ref: "ToolSpec" } },
33
+ uses_knowledge: { kind: :bool, default: false },
34
+ state_fields: { kind: :list, items: { kind: :ref, ref: "StateFieldSpec" } },
35
+ view: { kind: :ref, ref: "ViewSpec" },
36
+ events: { kind: :list, items: { kind: :ref, ref: "EventSpec" } }
37
+ }.freeze,
38
+ "KnowledgePush" => {
39
+ files: { kind: :list, items: { kind: :ref, ref: "KnowledgeFile" }, required: true }
40
+ }.freeze,
41
+ "KnowledgeGolden" => {
42
+ questions: { kind: :list, items: { kind: :ref, ref: "GoldenQuestion" }, required: true },
43
+ k: { kind: :int }
44
+ }.freeze,
45
+ "KnowledgeScore" => {
46
+ base: { kind: :str, required: true },
47
+ model: { kind: :str, required: true },
48
+ questions: { kind: :int, required: true },
49
+ k: { kind: :int, required: true },
50
+ recall_at_k: { kind: :float, required: true },
51
+ ndcg_at_10: { kind: :float, required: true },
52
+ took_ms: { kind: :float, required: true },
53
+ misses: { kind: :list, items: { kind: :ref, ref: "GoldenMiss" }, required: true }
54
+ }.freeze,
55
+ "KnowledgePushed" => {
56
+ base: { kind: :str, required: true },
57
+ chunks: { kind: :int, required: true },
58
+ took_ms: { kind: :float, required: true }
59
+ }.freeze,
60
+ "KnowledgeList" => {
61
+ bases: { kind: :list, items: { kind: :ref, ref: "KnowledgeBase" }, required: true }
62
+ }.freeze,
63
+ "ContactMemory" => {
64
+ facts: { kind: :list, items: { kind: :ref, ref: "ContactFact" }, required: true }
65
+ }.freeze,
66
+ "Forgotten" => {
67
+ forgotten: { kind: :int, required: true }
68
+ }.freeze,
69
+ "MemoryGolden" => {
70
+ questions: { kind: :list, items: { kind: :ref, ref: "MemoryQuestion" }, required: true },
71
+ k: { kind: :int }
72
+ }.freeze,
73
+ "MemoryScore" => {
74
+ model: { kind: :str, required: true },
75
+ questions: { kind: :int, required: true },
76
+ k: { kind: :int, required: true },
77
+ recall_at_k: { kind: :float, required: true },
78
+ ndcg_at_10: { kind: :float, required: true },
79
+ took_ms: { kind: :float, required: true },
80
+ misses: { kind: :list, items: { kind: :ref, ref: "MemoryMiss" }, required: true }
81
+ }.freeze,
82
+ "ExtractionCases" => {
83
+ cases: { kind: :list, items: { kind: :ref, ref: "ExtractionGolden" }, required: true }
84
+ }.freeze,
85
+ "ExtractionRun" => {
86
+ agent: { kind: :str, required: true },
87
+ model: { kind: :str, required: true },
88
+ cases: { kind: :int, required: true },
89
+ held: { kind: :int, required: true },
90
+ took_ms: { kind: :float, required: true },
91
+ results: { kind: :list, items: { kind: :ref, ref: "ExtractionJudged" }, required: true }
92
+ }.freeze,
93
+ }.freeze
94
+ end
95
+ end
96
+ end