signalwire-agents 0.1.31__py3-none-any.whl → 0.1.33__py3-none-any.whl

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.
@@ -6455,6 +6455,20 @@
6455
6455
  "type": "boolean",
6456
6456
  "description": "A boolean value, if set to `true`, the conversation history will be reset and the agent will only have the context of the original system prompt."
6457
6457
  },
6458
+ "enter_fillers": {
6459
+ "type": "array",
6460
+ "items": {
6461
+ "$ref": "#/$defs/FunctionFillers"
6462
+ },
6463
+ "description": "An array of objects that define the enter fillers for the context. Enter fillers are used when entering the context."
6464
+ },
6465
+ "exit_fillers": {
6466
+ "type": "array",
6467
+ "items": {
6468
+ "$ref": "#/$defs/FunctionFillers"
6469
+ },
6470
+ "description": "An array of objects that define the exit fillers for the context. Exit fillers are used when exiting the context."
6471
+ },
6458
6472
  "pom": {
6459
6473
  "type": "array",
6460
6474
  "items": {
@@ -6484,6 +6498,20 @@
6484
6498
  "type": "boolean",
6485
6499
  "description": "A boolean value, if set to `true`, the conversation history will be reset and the agent will only have the context of the original system prompt."
6486
6500
  },
6501
+ "enter_fillers": {
6502
+ "type": "array",
6503
+ "items": {
6504
+ "$ref": "#/$defs/FunctionFillers"
6505
+ },
6506
+ "description": "An array of objects that define the enter fillers for the context. Enter fillers are used when entering the context."
6507
+ },
6508
+ "exit_fillers": {
6509
+ "type": "array",
6510
+ "items": {
6511
+ "$ref": "#/$defs/FunctionFillers"
6512
+ },
6513
+ "description": "An array of objects that define the exit fillers for the context. Exit fillers are used when exiting the context."
6514
+ },
6487
6515
  "text": {
6488
6516
  "type": "string",
6489
6517
  "description": "The text to send to the agent."
@@ -6712,13 +6740,13 @@
6712
6740
  "ContextSteps": {
6713
6741
  "anyOf": [
6714
6742
  {
6715
- "$ref": "#/$defs/StepTypePOM"
6743
+ "$ref": "#/$defs/ContextPOMSteps"
6716
6744
  },
6717
6745
  {
6718
- "$ref": "#/$defs/StepTypeText"
6746
+ "$ref": "#/$defs/ContextTextSteps"
6719
6747
  }
6720
6748
  ],
6721
- "title": "ContextStepsParams union"
6749
+ "title": "Context step - supports either POM or text-based steps"
6722
6750
  },
6723
6751
  "StringProperty": {
6724
6752
  "type": "object",
@@ -7137,25 +7165,128 @@
7137
7165
  ],
7138
7166
  "title": "Action union"
7139
7167
  },
7140
- "StepTypePOM": {
7141
- "anyOf": [
7142
- {
7143
- "$ref": "#/$defs/POMEndStep"
7168
+ "ContextPOMSteps": {
7169
+ "type": "object",
7170
+ "properties": {
7171
+ "name": {
7172
+ "type": "string",
7173
+ "pattern": "^(?!next$).*$",
7174
+ "description": "The name of the step. The name must be unique within the context. The name is used for referencing the step in the context."
7144
7175
  },
7145
- {
7146
- "$ref": "#/$defs/POMValidSteps"
7176
+ "step_criteria": {
7177
+ "type": "string",
7178
+ "description": "The conditions that must be met for the conversation to proceed to the next step. \nIf a condition is not met, the conversation will not proceed to the next step.\nIt's **highly** recommended you create a custom criteria for the step to get the intended behavior. "
7179
+ },
7180
+ "functions": {
7181
+ "type": "array",
7182
+ "items": {
7183
+ "type": "string"
7184
+ },
7185
+ "description": "An array of strings, where each string is the name of a SWAIG.function that can be executed from this step."
7186
+ },
7187
+ "valid_contexts": {
7188
+ "type": "array",
7189
+ "items": {
7190
+ "type": "string"
7191
+ },
7192
+ "description": "An array of valid contexts that the conversation can transition to from this step."
7193
+ },
7194
+ "skip_user_turn": {
7195
+ "anyOf": [
7196
+ {
7197
+ "type": "boolean"
7198
+ },
7199
+ {
7200
+ "$ref": "#/$defs/SWMLVar"
7201
+ }
7202
+ ],
7203
+ "description": "A boolean value, if set to `true`, will skip the user's turn to respond in the conversation and proceed to the next step."
7204
+ },
7205
+ "end": {
7206
+ "type": "boolean",
7207
+ "description": "A boolean value, if set to `true`, will end the contexts conversation and transition to a normal interaction."
7208
+ },
7209
+ "valid_steps": {
7210
+ "type": "array",
7211
+ "items": {
7212
+ "type": "string"
7213
+ },
7214
+ "description": "An array of valid steps that the conversation can proceed to from this step.\nIf the array is empty, or the `valid_steps` key is not present, the conversation will proceed to the next step in the context."
7215
+ },
7216
+ "pom": {
7217
+ "type": "array",
7218
+ "items": {
7219
+ "$ref": "#/$defs/POM"
7220
+ },
7221
+ "description": "An array of objects that define the POM for the step. POM is the Post-Prompt Object Model, which is used to define the flow of the conversation."
7147
7222
  }
7148
- ]
7223
+ },
7224
+ "required": [
7225
+ "name",
7226
+ "pom"
7227
+ ],
7228
+ "title": "Context step with POM (Post-Prompt Object Model)",
7229
+ "unevaluatedProperties": false
7149
7230
  },
7150
- "StepTypeText": {
7151
- "anyOf": [
7152
- {
7153
- "$ref": "#/$defs/TextEndStep"
7231
+ "ContextTextSteps": {
7232
+ "type": "object",
7233
+ "properties": {
7234
+ "name": {
7235
+ "type": "string",
7236
+ "pattern": "^(?!next$).*$",
7237
+ "description": "The name of the step. The name must be unique within the context. The name is used for referencing the step in the context."
7154
7238
  },
7155
- {
7156
- "$ref": "#/$defs/TextValidSteps"
7239
+ "step_criteria": {
7240
+ "type": "string",
7241
+ "description": "The conditions that must be met for the conversation to proceed to the next step. \nIf a condition is not met, the conversation will not proceed to the next step.\nIt's **highly** recommended you create a custom criteria for the step to get the intended behavior. "
7242
+ },
7243
+ "functions": {
7244
+ "type": "array",
7245
+ "items": {
7246
+ "type": "string"
7247
+ },
7248
+ "description": "An array of strings, where each string is the name of a SWAIG.function that can be executed from this step."
7249
+ },
7250
+ "valid_contexts": {
7251
+ "type": "array",
7252
+ "items": {
7253
+ "type": "string"
7254
+ },
7255
+ "description": "An array of valid contexts that the conversation can transition to from this step."
7256
+ },
7257
+ "skip_user_turn": {
7258
+ "anyOf": [
7259
+ {
7260
+ "type": "boolean"
7261
+ },
7262
+ {
7263
+ "$ref": "#/$defs/SWMLVar"
7264
+ }
7265
+ ],
7266
+ "description": "A boolean value, if set to `true`, will skip the user's turn to respond in the conversation and proceed to the next step."
7267
+ },
7268
+ "end": {
7269
+ "type": "boolean",
7270
+ "description": "A boolean value, if set to `true`, will end the contexts conversation and transition to a normal interaction."
7271
+ },
7272
+ "valid_steps": {
7273
+ "type": "array",
7274
+ "items": {
7275
+ "type": "string"
7276
+ },
7277
+ "description": "An array of valid steps that the conversation can proceed to from this step.\nIf the array is empty, or the `valid_steps` key is not present, the conversation will proceed to the next step in the context."
7278
+ },
7279
+ "text": {
7280
+ "type": "string",
7281
+ "description": "The prompt or instructions given to the AI at this step."
7157
7282
  }
7158
- ]
7283
+ },
7284
+ "required": [
7285
+ "name",
7286
+ "text"
7287
+ ],
7288
+ "title": "Context step with text prompt",
7289
+ "unevaluatedProperties": false
7159
7290
  },
7160
7291
  "StringFormat": {
7161
7292
  "type": "string",
@@ -7469,230 +7600,6 @@
7469
7600
  ],
7470
7601
  "title": "UserInputAction object",
7471
7602
  "unevaluatedProperties": false
7472
- },
7473
- "POMEndStep": {
7474
- "type": "object",
7475
- "properties": {
7476
- "name": {
7477
- "type": "string",
7478
- "pattern": "^(?!next$).*$",
7479
- "description": "The name of the step. The name must be unique within the context. The name is used for referencing the step in the context."
7480
- },
7481
- "step_criteria": {
7482
- "type": "string",
7483
- "description": "The conditions that must be met for the conversation to proceed to the next step. \nIf a condition is not met, the conversation will not proceed to the next step.\nIt's **highly** recommended you create a custom criteria for the step to get the intended behavior. "
7484
- },
7485
- "functions": {
7486
- "type": "array",
7487
- "items": {
7488
- "type": "string"
7489
- },
7490
- "description": "An array of strings, where each string is the name of a SWAIG.function that can be executed from this step."
7491
- },
7492
- "valid_contexts": {
7493
- "type": "array",
7494
- "items": {
7495
- "type": "string"
7496
- },
7497
- "description": "An array of valid contexts that the conversation can transition to from this step."
7498
- },
7499
- "skip_user_turn": {
7500
- "anyOf": [
7501
- {
7502
- "type": "boolean"
7503
- },
7504
- {
7505
- "$ref": "#/$defs/SWMLVar"
7506
- }
7507
- ],
7508
- "description": "A boolean value, if set to `true`, will skip the user's turn to respond in the conversation and proceed to the next step."
7509
- },
7510
- "pom": {
7511
- "type": "array",
7512
- "items": {
7513
- "$ref": "#/$defs/POM"
7514
- },
7515
- "description": "An array of objects that define the POM for the step. POM is the Post-Prompt Object Model, which is used to define the flow of the conversation."
7516
- },
7517
- "end": {
7518
- "type": "boolean",
7519
- "description": "A boolean value, if set to `true`, will end the contexts conversation and transition to a normal interaction."
7520
- }
7521
- },
7522
- "required": [
7523
- "name",
7524
- "pom"
7525
- ],
7526
- "title": "ContextStepsEnd object",
7527
- "unevaluatedProperties": false
7528
- },
7529
- "POMValidSteps": {
7530
- "type": "object",
7531
- "properties": {
7532
- "name": {
7533
- "type": "string",
7534
- "pattern": "^(?!next$).*$",
7535
- "description": "The name of the step. The name must be unique within the context. The name is used for referencing the step in the context."
7536
- },
7537
- "step_criteria": {
7538
- "type": "string",
7539
- "description": "The conditions that must be met for the conversation to proceed to the next step. \nIf a condition is not met, the conversation will not proceed to the next step.\nIt's **highly** recommended you create a custom criteria for the step to get the intended behavior. "
7540
- },
7541
- "functions": {
7542
- "type": "array",
7543
- "items": {
7544
- "type": "string"
7545
- },
7546
- "description": "An array of strings, where each string is the name of a SWAIG.function that can be executed from this step."
7547
- },
7548
- "valid_contexts": {
7549
- "type": "array",
7550
- "items": {
7551
- "type": "string"
7552
- },
7553
- "description": "An array of valid contexts that the conversation can transition to from this step."
7554
- },
7555
- "skip_user_turn": {
7556
- "anyOf": [
7557
- {
7558
- "type": "boolean"
7559
- },
7560
- {
7561
- "$ref": "#/$defs/SWMLVar"
7562
- }
7563
- ],
7564
- "description": "A boolean value, if set to `true`, will skip the user's turn to respond in the conversation and proceed to the next step."
7565
- },
7566
- "pom": {
7567
- "type": "array",
7568
- "items": {
7569
- "$ref": "#/$defs/POM"
7570
- },
7571
- "description": "An array of objects that define the POM for the step. POM is the Post-Prompt Object Model, which is used to define the flow of the conversation."
7572
- },
7573
- "valid_steps": {
7574
- "type": "array",
7575
- "items": {
7576
- "type": "string"
7577
- },
7578
- "description": "An array of valid steps that the conversation can proceed to from this step.\nIf the array is empty, or the `valid_steps` key is not present, the conversation will proceed to the next step in the context."
7579
- }
7580
- },
7581
- "required": [
7582
- "name",
7583
- "pom"
7584
- ],
7585
- "title": "ContextStepsValidSteps object",
7586
- "unevaluatedProperties": false
7587
- },
7588
- "TextEndStep": {
7589
- "type": "object",
7590
- "properties": {
7591
- "name": {
7592
- "type": "string",
7593
- "pattern": "^(?!next$).*$",
7594
- "description": "The name of the step. The name must be unique within the context. The name is used for referencing the step in the context."
7595
- },
7596
- "step_criteria": {
7597
- "type": "string",
7598
- "description": "The conditions that must be met for the conversation to proceed to the next step. \nIf a condition is not met, the conversation will not proceed to the next step.\nIt's **highly** recommended you create a custom criteria for the step to get the intended behavior. "
7599
- },
7600
- "functions": {
7601
- "type": "array",
7602
- "items": {
7603
- "type": "string"
7604
- },
7605
- "description": "An array of strings, where each string is the name of a SWAIG.function that can be executed from this step."
7606
- },
7607
- "valid_contexts": {
7608
- "type": "array",
7609
- "items": {
7610
- "type": "string"
7611
- },
7612
- "description": "An array of valid contexts that the conversation can transition to from this step."
7613
- },
7614
- "skip_user_turn": {
7615
- "anyOf": [
7616
- {
7617
- "type": "boolean"
7618
- },
7619
- {
7620
- "$ref": "#/$defs/SWMLVar"
7621
- }
7622
- ],
7623
- "description": "A boolean value, if set to `true`, will skip the user's turn to respond in the conversation and proceed to the next step."
7624
- },
7625
- "text": {
7626
- "type": "string",
7627
- "description": "The prompt or instructions given to the AI at this step."
7628
- },
7629
- "end": {
7630
- "type": "boolean",
7631
- "description": "A boolean value, if set to `true`, will end the contexts conversation and transition to a normal interaction."
7632
- }
7633
- },
7634
- "required": [
7635
- "name",
7636
- "text"
7637
- ],
7638
- "title": "ContextStepsBase object",
7639
- "unevaluatedProperties": false
7640
- },
7641
- "TextValidSteps": {
7642
- "type": "object",
7643
- "properties": {
7644
- "name": {
7645
- "type": "string",
7646
- "pattern": "^(?!next$).*$",
7647
- "description": "The name of the step. The name must be unique within the context. The name is used for referencing the step in the context."
7648
- },
7649
- "step_criteria": {
7650
- "type": "string",
7651
- "description": "The conditions that must be met for the conversation to proceed to the next step. \nIf a condition is not met, the conversation will not proceed to the next step.\nIt's **highly** recommended you create a custom criteria for the step to get the intended behavior. "
7652
- },
7653
- "functions": {
7654
- "type": "array",
7655
- "items": {
7656
- "type": "string"
7657
- },
7658
- "description": "An array of strings, where each string is the name of a SWAIG.function that can be executed from this step."
7659
- },
7660
- "valid_contexts": {
7661
- "type": "array",
7662
- "items": {
7663
- "type": "string"
7664
- },
7665
- "description": "An array of valid contexts that the conversation can transition to from this step."
7666
- },
7667
- "skip_user_turn": {
7668
- "anyOf": [
7669
- {
7670
- "type": "boolean"
7671
- },
7672
- {
7673
- "$ref": "#/$defs/SWMLVar"
7674
- }
7675
- ],
7676
- "description": "A boolean value, if set to `true`, will skip the user's turn to respond in the conversation and proceed to the next step."
7677
- },
7678
- "text": {
7679
- "type": "string",
7680
- "description": "The prompt or instructions given to the AI at this step."
7681
- },
7682
- "valid_steps": {
7683
- "type": "array",
7684
- "items": {
7685
- "type": "string"
7686
- },
7687
- "description": "An array of valid steps that the conversation can proceed to from this step.\nIf the array is empty, or the `valid_steps` key is not present, the conversation will proceed to the next step in the context."
7688
- }
7689
- },
7690
- "required": [
7691
- "name",
7692
- "text"
7693
- ],
7694
- "title": "ContextStepsBase object",
7695
- "unevaluatedProperties": false
7696
7603
  }
7697
7604
  },
7698
7605
  "unevaluatedProperties": false
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: signalwire_agents
3
- Version: 0.1.31
3
+ Version: 0.1.33
4
4
  Summary: SignalWire AI Agents SDK
5
5
  Author-email: SignalWire Team <info@signalwire.com>
6
6
  License: MIT
@@ -98,12 +98,16 @@ Dynamic: license-file
98
98
  <img src="https://img.shields.io/github/stars/signalwire/signalwire-agents" alt="GitHub Stars" href="https://github.com/signalwire/docs"/>
99
99
  </div>
100
100
 
101
+ <br/>
102
+
103
+ <img src="https://github.com/user-attachments/assets/c2510c86-ae03-42a9-be06-ab9bcea948e1" alt="Sign Up" href="https://signalwire.com/signup" height="65"/>
104
+
101
105
  </div>
102
106
 
103
107
  ## Features
104
108
 
105
109
  | | |
106
- |-------------------------------|-----------------------------------------------------------------------------|
110
+ |-------------------------------|:-----------------------------------------------------------------------------:|
107
111
  | 🤖 **Self-Contained Agents** | Each agent is both a web app and an AI persona |
108
112
  | 📝 **Prompt Object Model** | Structured prompt composition using POM |
109
113
  | ⚙️ **SWAIG Integration** | Easily define and handle AI tools/functions |
@@ -117,24 +121,84 @@ Dynamic: license-file
117
121
  | � **Modular Skills System** | Add capabilities to agents with simple one-liner calls |
118
122
  | 🔍 **Local Search System** | Offline document search with vector similarity and keyword search |
119
123
 
120
- <picture>
121
- <source
122
- media="(prefers-color-scheme: dark)"
123
- srcset="
124
- https://api.star-history.com/svg?repos=signalwire/signalwire-agents&type=Date&theme=dark
125
- "
126
- />
127
- <source
128
- media="(prefers-color-scheme: light)"
129
- srcset="
130
- https://api.star-history.com/svg?repos=signalwire/signalwire-agents&type=Date
131
- "
132
- />
133
- <img
134
- alt="Star History Chart"
135
- src="https://api.star-history.com/svg?repos=signalwire/signalwire-agents&type=Date"
136
- />
137
- </picture>
124
+ ## Installation
125
+
126
+ ### Basic Installation
127
+
128
+ ```bash
129
+ pip install signalwire-agents
130
+ ```
131
+
132
+ ### Optional Search Functionality
133
+
134
+ The SDK includes optional local search capabilities that can be installed separately to avoid adding large dependencies to the base installation:
135
+
136
+ #### Search Installation Options
137
+
138
+ ```bash
139
+ # Query existing .swsearch files only (smallest footprint)
140
+ pip install signalwire-agents[search-queryonly]
141
+
142
+ # Basic search (vector search + keyword search + building indexes)
143
+ pip install signalwire-agents[search]
144
+
145
+ # Full search with document processing (PDF, DOCX, etc.)
146
+ pip install signalwire-agents[search-full]
147
+
148
+ # Advanced NLP features (includes spaCy)
149
+ pip install signalwire-agents[search-nlp]
150
+
151
+ # All search features
152
+ pip install signalwire-agents[search-all]
153
+ ```
154
+
155
+ #### What Each Option Includes
156
+
157
+ | Option | Size | Features |
158
+ |--------|------|----------|
159
+ | `search-queryonly` | ~400MB | Query existing .swsearch files only (no building/processing) |
160
+ | `search` | ~500MB | Vector embeddings, keyword search, basic text processing |
161
+ | `search-full` | ~600MB | + PDF, DOCX, Excel, PowerPoint, HTML, Markdown processing |
162
+ | `search-nlp` | ~600MB | + Advanced spaCy NLP features |
163
+ | `search-all` | ~700MB | All search features combined |
164
+
165
+ **When to use `search-queryonly`:**
166
+ - Production containers with pre-built `.swsearch` files
167
+ - Lambda/serverless deployments
168
+ - Agents that only need to query knowledge bases (not build them)
169
+ - Smaller deployment footprint requirements
170
+
171
+ #### Search Features
172
+
173
+ - **Local/Offline Search**: No external API dependencies
174
+ - **Hybrid Search**: Vector similarity + keyword search
175
+ - **Smart Document Processing**: Markdown, Python, PDF, DOCX, etc.
176
+ - **Multiple Languages**: English, Spanish, with extensible framework
177
+ - **CLI Tools**: Build search indexes from document directories
178
+ - **HTTP API**: Standalone or embedded search service
179
+
180
+ #### Usage Example
181
+
182
+ ```python
183
+ # Only available with search extras installed
184
+ from signalwire_agents.search import IndexBuilder, SearchEngine
185
+
186
+ # Build search index
187
+ builder = IndexBuilder()
188
+ builder.build_index(
189
+ source_dir="./docs",
190
+ output_file="knowledge.swsearch",
191
+ file_types=['md', 'txt', 'pdf']
192
+ )
193
+
194
+ # Search documents
195
+ engine = SearchEngine("knowledge.swsearch")
196
+ results = engine.search(
197
+ query_vector=embeddings,
198
+ enhanced_text="search query",
199
+ count=5
200
+ )
201
+ ```
138
202
 
139
203
  <details>
140
204
  <summary><h2>Documentation</h2></summary>
@@ -529,85 +593,6 @@ agent.serve()
529
593
 
530
594
  For detailed documentation and advanced examples, see [Contexts and Steps Guide](docs/contexts_guide.md).
531
595
 
532
- ### Installation
533
-
534
- #### Basic Installation
535
-
536
- ```bash
537
- pip install signalwire-agents
538
- ```
539
-
540
- #### Optional Search Functionality
541
-
542
- The SDK includes optional local search capabilities that can be installed separately to avoid adding large dependencies to the base installation:
543
-
544
- ##### Search Installation Options
545
-
546
- ```bash
547
- # Query existing .swsearch files only (smallest footprint)
548
- pip install signalwire-agents[search-queryonly]
549
-
550
- # Basic search (vector search + keyword search + building indexes)
551
- pip install signalwire-agents[search]
552
-
553
- # Full search with document processing (PDF, DOCX, etc.)
554
- pip install signalwire-agents[search-full]
555
-
556
- # Advanced NLP features (includes spaCy)
557
- pip install signalwire-agents[search-nlp]
558
-
559
- # All search features
560
- pip install signalwire-agents[search-all]
561
- ```
562
-
563
- ##### What Each Option Includes
564
-
565
- | Option | Size | Features |
566
- |--------|------|----------|
567
- | `search-queryonly` | ~400MB | Query existing .swsearch files only (no building/processing) |
568
- | `search` | ~500MB | Vector embeddings, keyword search, basic text processing |
569
- | `search-full` | ~600MB | + PDF, DOCX, Excel, PowerPoint, HTML, Markdown processing |
570
- | `search-nlp` | ~600MB | + Advanced spaCy NLP features |
571
- | `search-all` | ~700MB | All search features combined |
572
-
573
- **When to use `search-queryonly`:**
574
- - Production containers with pre-built `.swsearch` files
575
- - Lambda/serverless deployments
576
- - Agents that only need to query knowledge bases (not build them)
577
- - Smaller deployment footprint requirements
578
-
579
- ##### Search Features
580
-
581
- - **Local/Offline Search**: No external API dependencies
582
- - **Hybrid Search**: Vector similarity + keyword search
583
- - **Smart Document Processing**: Markdown, Python, PDF, DOCX, etc.
584
- - **Multiple Languages**: English, Spanish, with extensible framework
585
- - **CLI Tools**: Build search indexes from document directories
586
- - **HTTP API**: Standalone or embedded search service
587
-
588
- ##### Usage Example
589
-
590
- ```python
591
- # Only available with search extras installed
592
- from signalwire_agents.search import IndexBuilder, SearchEngine
593
-
594
- # Build search index
595
- builder = IndexBuilder()
596
- builder.build_index(
597
- source_dir="./docs",
598
- output_file="knowledge.swsearch",
599
- file_types=['md', 'txt', 'pdf']
600
- )
601
-
602
- # Search documents
603
- engine = SearchEngine("knowledge.swsearch")
604
- results = engine.search(
605
- query_vector=embeddings,
606
- enhanced_text="search query",
607
- count=5
608
- )
609
- ```
610
-
611
596
  ### Quick Start
612
597
 
613
598
  ```python