pinecall 0.0.4 → 0.0.5

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a2a10f0e074b767542af49bb9d9d6ae3251e230b371debee5d5e5628c92f67b2
4
- data.tar.gz: 81be17cb2c53a7c5c6fc3d188f7b38abd16f265afafc8c3d0a089933fb126809
3
+ metadata.gz: 0571abe150af681966473dd3a8e8123137743bbecf63d6c4144632f986eb304a
4
+ data.tar.gz: 55c632d74f2440250049ecc84bad96d611b7d270cea5130d01154674b84761ad
5
5
  SHA512:
6
- metadata.gz: f7baea4bcf9f7b72c71d24e24ddf698e3afa5d06863d60dbd32943b8648705857e8b209b43130fc1c9e435673f03666f50a9ee7694f970c19f6ca2eb868e4ef9
7
- data.tar.gz: e966301764b4ce05390cae2821339012f95106dea20593fc0e1ffc00106c48d348ef6e1f56ab46ca5ad6ce0f67fb1bb5072f071334f2d0ac59fdc8309269bdb3
6
+ metadata.gz: c476476012157ce39d48b2e1b07ccd33c5533d217ff2d33037e423d05b48a49986e556638fbca2410890c739aeb00b13da0076415e1c2f62c6a8f1a0452a239e
7
+ data.tar.gz: d42a9901721b9e179fbddd3ffba1b2c4ff1805c3abceaaafe4b5d77aecf0990dc1265685e762164cdbad4d11b3a4b77a20f6bba5ceeab06ccd5b95cc8ee7445e
data/CHANGELOG.md CHANGED
@@ -6,6 +6,18 @@ version number is the human's call.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.0.5] — The class declares its voice, its models and the rest, and wins (2026-10-10)
10
+
11
+ - **A class declares its environment again, and it wins over the settings.** Class macros `voice
12
+ "<vendor>", "<id>", model:`, `llm "<vendor>/<model>", temperature:`, `stt "<vendor>/<model>"`,
13
+ `language`, `greeting`, `hangup`, `turn`, `says`, `hears`, `knowledge`, `docs`, `memory` and
14
+ `record`, sent on `agent.configure`. A field the class declares is not read from the settings,
15
+ the console shows it "set by the class", and `pinecall agent set` of it is refused. Needs runtime
16
+ 0.1.42.
17
+ - `builds:` and `options:` on `voice`, `llm` and `stt`: a class of the vendor's plugin and its
18
+ keyword arguments (`llm "openai/gpt-5.4-mini", builds: "responses.LLM", options: { use_websocket:
19
+ true }`), only on the org's own key for the vendor. The refusal that named a CLI verb is gone.
20
+
9
21
  ## [0.0.4] — A tool announces itself (2026-10-09)
10
22
 
11
23
  ### Added
@@ -4,35 +4,28 @@ module Pinecall
4
4
  class Agent
5
5
  # Class-level configuration (state lives on the instance).
6
6
  #
7
- # A class declares its contract: tools, state, view. Runtime settings (voice, models,
8
- # language, greeting, memory, knowledge) belong to the world and are refused at load with the
9
- # CLI command that sets them.
7
+ # A class declares its contract: tools, state, view. It may also declare its environment —
8
+ # voice, models, language, greeting, memory, knowledge — and whatever it declares wins over the
9
+ # agent's settings for that field.
10
10
  module Config
11
11
  # `phone`, `whatsapp` and `web` are accepted for compatibility but ignored. `channel_rules
12
12
  # false` leaves the `<channel>` block out of the prompt.
13
13
  AS_WRITTEN = %i[phone whatsapp web channel_rules].freeze
14
14
 
15
- # Settings that moved to the world, and the command that sets each. Must match the
16
- # TypeScript package's `THE_WORLDS`.
17
- THE_WORLDS = {
18
- voice: "pinecall agent set --voice <name>",
19
- llm: "pinecall agent set --llm <vendor/model>",
20
- stt: "pinecall agent set --stt <vendor>",
21
- language: "pinecall agent set --language <tag>",
22
- greeting: "pinecall agent set --greeting '…' (or --reply '…')",
23
- hangup: "pinecall agent set --hangup '…'",
24
- says: "pinecall lexicon add <word> --say '…'",
25
- hears: "pinecall lexicon hear <word> …",
26
- memory: "pinecall memory policy --remember '…' --forget '…'",
27
- record: "pinecall agent set --record on|off",
28
- knowledge: "pinecall agent knowledge edit — what the agent knows by heart is a setting, not a file",
29
- docs: "pinecall docs push, then pinecall docs attach <base>"
30
- }.freeze
15
+ # The settings a class may declare. Must match the TypeScript package's `ENVIRONMENT`.
16
+ ENVIRONMENT = %i[language voice llm stt greeting hangup turn says hears knowledge docs memory record].freeze
17
+
18
+ # The ones written as the wire's own fields, keyword by keyword: `greeting say: "…"`.
19
+ AS_KEYWORDS = %i[greeting hangup turn knowledge docs memory].freeze
20
+
21
+ NO_VOICE = "a voice the class declares names its vendor and the voice: voice \"<vendor>\", \"<voice id>\""
31
22
 
32
23
  NOTHING = Object.new.freeze
33
24
 
34
- def self.moved_to_the_world(field)
35
- "`#{field}` is the world's now, not the class's: #{THE_WORLDS.fetch(field)} — remove it from the class"
25
+ # `vendor/model`, or a vendor alone; the model id keeps every slash after the vendor's.
26
+ def self.model_of(named)
27
+ provider, _slash, model = named.to_s.partition("/")
28
+ { provider:, model: }
36
29
  end
37
30
 
38
31
  module Declaring
@@ -45,13 +38,61 @@ module Pinecall
45
38
  end
46
39
  end
47
40
 
48
- # World settings raise at load, naming the command that sets them.
49
- THE_WORLDS.each_key do |field|
50
- define_method(field) do |*_said, **_options|
51
- raise DeclarationRefused, Config.moved_to_the_world(field)
41
+ # The voice, by its vendor and the vendor's own id for it. Declared, it wins over the
42
+ # agent's settings; `builds` and `options` reach the vendor's plugin.
43
+ def voice(provider = NOTHING, voice_id = nil, model: nil, builds: nil, options: nil)
44
+ return environment[:voice] if provider.equal?(NOTHING)
45
+ raise DeclarationRefused, NO_VOICE if voice_id.nil?
46
+
47
+ environment[:voice] = { provider:, voice_id:, model:, builds:, options: }.compact
48
+ end
49
+
50
+ # The model that answers, `vendor/model` or a vendor alone:
51
+ # `llm "openai/gpt-5.4-mini", builds: "responses.LLM", options: { use_websocket: true }`.
52
+ def llm(model = NOTHING, temperature: nil, builds: nil, options: nil)
53
+ return environment[:llm] if model.equal?(NOTHING)
54
+
55
+ environment[:llm] = { **Config.model_of(model), temperature:, builds:, options: }.compact
56
+ end
57
+
58
+ # The ears, `vendor/model` or a vendor alone.
59
+ def stt(model = NOTHING, builds: nil, options: nil)
60
+ return environment[:stt] if model.equal?(NOTHING)
61
+
62
+ environment[:stt] = { **Config.model_of(model), builds:, options: }.compact
63
+ end
64
+
65
+ # `language "es"`, `says [{ word: "GSA", spoken: "ge ese a" }]`, `record false`.
66
+ %i[language says record].each do |field|
67
+ define_method(field) do |value = NOTHING|
68
+ return environment[field] if value.equal?(NOTHING)
69
+
70
+ environment[field] = value
71
+ end
72
+ end
73
+
74
+ # The words the ears must know: `hears "Vidal", "Sanitas"`.
75
+ def hears(*words)
76
+ return environment[:hears] if words.empty?
77
+
78
+ environment[:hears] = words.flatten
79
+ end
80
+
81
+ # `greeting say: "…"`, `hangup when: "…"`, `turn endpointing_ms: 300`, `docs base: "…", k: 4`,
82
+ # `memory remember: […], forget: […]`, `knowledge path: "…", text: "…"`.
83
+ AS_KEYWORDS.each do |field|
84
+ define_method(field) do |**given|
85
+ return environment[field] if given.empty?
86
+
87
+ environment[field] = given
52
88
  end
53
89
  end
54
90
 
91
+ # What the class declares of its environment, including inherited values.
92
+ def environment
93
+ @environment ||= superclass.respond_to?(:environment) ? superclass.environment.dup : {}
94
+ end
95
+
55
96
  # Set the class docstring explicitly, for classes with no source file.
56
97
  def doc(text = NOTHING)
57
98
  return Doc.for_class(self) if text.equal?(NOTHING)
@@ -84,9 +125,10 @@ module Pinecall
84
125
  @slug = name
85
126
  end
86
127
 
87
- # The AgentConfig sent to the gateway.
128
+ # The AgentConfig sent to the gateway: the contract, and the environment the class declares.
88
129
  def wire_config(tools: nil)
89
130
  {
131
+ **environment,
90
132
  prompt: Prompt::FRAMEWORK,
91
133
  tools:,
92
134
  state_fields: state_field_specs,
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Pinecall
4
4
  # Set by the maintainer at release time.
5
- VERSION = "0.0.4"
5
+ VERSION = "0.0.5"
6
6
  end
@@ -14,12 +14,16 @@ module Pinecall
14
14
  name: { kind: :str },
15
15
  provider: { kind: :str },
16
16
  model: { kind: :str },
17
- voice_id: { kind: :str }
17
+ voice_id: { kind: :str },
18
+ builds: { kind: :str },
19
+ options: { kind: :json }
18
20
  }.freeze,
19
21
  "ModelConfig" => {
20
22
  provider: { kind: :str, required: true },
21
23
  model: { kind: :str, required: true },
22
- temperature: { kind: :float }
24
+ temperature: { kind: :float },
25
+ builds: { kind: :str },
26
+ options: { kind: :json }
23
27
  }.freeze,
24
28
  "TurnConfig" => {
25
29
  min_interruption_words: { kind: :int },
@@ -18,6 +18,7 @@ module Pinecall
18
18
  }.freeze,
19
19
  "AgentConfig" => {
20
20
  prompt: { kind: :list, items: { kind: :ref, ref: "PromptBlockSpec" } },
21
+ language: { kind: :str },
21
22
  greeting: { kind: :ref, ref: "GreetingConfig" },
22
23
  voice: { kind: :ref, ref: "VoiceConfig" },
23
24
  llm: { kind: :ref, ref: "ModelConfig" },
data/sig/pinecall.rbs CHANGED
@@ -84,18 +84,21 @@ module Pinecall
84
84
  def self.panel: (?String) { (Panel::Who) -> void } -> Panel::Declared
85
85
  def self.declared_panel: () -> Panel::Declared?
86
86
 
87
- # the world's, refused at load with the verb that sets each
88
- def self.voice: (*untyped, **untyped) -> bot
89
- def self.llm: (*untyped, **untyped) -> bot
90
- def self.stt: (*untyped, **untyped) -> bot
91
- def self.greeting: (*untyped, **untyped) -> bot
92
- def self.hangup: (*untyped, **untyped) -> bot
93
- def self.says: (*untyped, **untyped) -> bot
94
- def self.hears: (*untyped, **untyped) -> bot
95
- def self.memory: (*untyped, **untyped) -> bot
96
- def self.record: (*untyped, **untyped) -> bot
97
- def self.knowledge: (*untyped, **untyped) -> bot
98
- def self.docs: (*untyped, **untyped) -> bot
87
+ # the environment the class declares, each over the agent's settings
88
+ def self.voice: (?String, ?String?, ?model: String?, ?builds: String?, ?options: Hash[untyped, untyped]?) -> untyped
89
+ def self.llm: (?String, ?temperature: Float?, ?builds: String?, ?options: Hash[untyped, untyped]?) -> untyped
90
+ def self.stt: (?String, ?builds: String?, ?options: Hash[untyped, untyped]?) -> untyped
91
+ def self.language: (?String) -> untyped
92
+ def self.says: (?Array[Hash[Symbol, String]]) -> untyped
93
+ def self.record: (?bool) -> untyped
94
+ def self.hears: (*String | Array[String]) -> untyped
95
+ def self.greeting: (**untyped) -> untyped
96
+ def self.hangup: (**untyped) -> untyped
97
+ def self.turn: (**untyped) -> untyped
98
+ def self.knowledge: (**untyped) -> untyped
99
+ def self.docs: (**untyped) -> untyped
100
+ def self.memory: (**untyped) -> untyped
101
+ def self.environment: () -> Hash[Symbol, untyped]
99
102
 
100
103
  def self.doc: (?String) -> String?
101
104
  def self.slug: (?String) -> String
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pinecall
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pinecall