airia 0.1.12__py3-none-any.whl → 0.1.13__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.
@@ -1,9 +1,21 @@
1
+ """
2
+ Pydantic models for pipeline configuration API responses.
3
+
4
+ This module defines comprehensive data structures for pipeline configuration exports,
5
+ including all components like agents, models, tools, data sources, and deployment settings.
6
+ """
1
7
  from typing import Any, Dict, List, Optional
2
8
 
3
9
  from pydantic import BaseModel, Field
4
10
 
5
11
 
6
12
  class Metadata(BaseModel):
13
+ """
14
+ Pipeline metadata and export configuration.
15
+
16
+ Contains version information, export settings, and descriptive metadata
17
+ about the pipeline configuration.
18
+ """
7
19
  id: str
8
20
  export_version: str = Field(alias="exportVersion")
9
21
  tagline: Optional[str] = None
@@ -17,6 +29,12 @@ class Metadata(BaseModel):
17
29
 
18
30
 
19
31
  class Agent(BaseModel):
32
+ """
33
+ AI agent configuration and workflow definition.
34
+
35
+ Represents the core agent that executes the pipeline, including its
36
+ identity, industry specialization, and step-by-step workflow configuration.
37
+ """
20
38
  name: str
21
39
  execution_name: str = Field(alias="executionName")
22
40
  agent_description: Optional[str] = Field(alias="agentDescription", default=None)
@@ -91,6 +109,12 @@ class Tool(BaseModel):
91
109
 
92
110
 
93
111
  class Model(BaseModel):
112
+ """
113
+ Language model configuration and deployment settings.
114
+
115
+ Defines an AI model used in the pipeline, including its deployment details,
116
+ pricing configuration, authentication settings, and capabilities.
117
+ """
94
118
  id: str
95
119
  display_name: str = Field(alias="displayName")
96
120
  model_name: str = Field(alias="modelName")
@@ -167,6 +191,13 @@ class DataSource(BaseModel):
167
191
 
168
192
 
169
193
  class GetPipelineConfigResponse(BaseModel):
194
+ """
195
+ Complete pipeline configuration export response.
196
+
197
+ This is the root response model containing all components of a pipeline
198
+ configuration, including the agent definition, associated resources,
199
+ and deployment settings.
200
+ """
170
201
  metadata: Metadata
171
202
  agent: Agent
172
203
  data_sources: Optional[List[DataSource]] = Field(
@@ -1,3 +1,9 @@
1
+ """
2
+ Pydantic models for project management API responses.
3
+
4
+ This module defines the data structures returned by project-related endpoints,
5
+ including project listings and associated pipeline information.
6
+ """
1
7
  from datetime import datetime
2
8
  from typing import Any, List, Optional
3
9
 
@@ -5,11 +11,49 @@ from pydantic import BaseModel, Field
5
11
 
6
12
 
7
13
  class Pipeline(BaseModel):
14
+ """
15
+ Basic pipeline information associated with a project.
16
+
17
+ Represents a simplified view of pipeline data within project contexts,
18
+ containing only essential identification information.
19
+ """
8
20
  id: str
9
21
  name: str
10
22
 
11
23
 
12
24
  class ProjectItem(BaseModel):
25
+ """
26
+ Comprehensive project information and metadata.
27
+
28
+ This model represents a complete project entity with all associated resources,
29
+ budget information, security settings, and organizational details. Projects
30
+ serve as containers for pipelines, models, data sources, and other AI resources.
31
+
32
+ Attributes:
33
+ tenant_id: Unique identifier for the tenant/organization
34
+ created_at: Timestamp when the project was created
35
+ require_classification: Whether data classification is required
36
+ budget_amount: Optional budget limit for the project
37
+ budget_period: Time period for budget calculations
38
+ budget_alert: Budget alert threshold configuration
39
+ budget_stop: Whether to stop operations when budget is exceeded
40
+ used_budget_amount: Amount of budget currently consumed
41
+ resume_ends_at: When the project resumption period ends
42
+ updated_at: Timestamp of last project modification
43
+ pipelines: List of pipelines associated with this project
44
+ models: AI models available in this project
45
+ data_sources: Data sources configured for this project
46
+ prompts: Prompt templates available in this project
47
+ api_keys: API key configurations for external services
48
+ memories: Memory/context storage configurations
49
+ project_icon: Base64 encoded project icon image
50
+ project_icon_id: Unique identifier for the project icon
51
+ description: Human-readable project description
52
+ project_type: Classification of project type
53
+ classifications: Data classification settings
54
+ id: Unique project identifier
55
+ name: Human-readable project name
56
+ """
13
57
  tenant_id: str = Field(alias="tenantId")
14
58
  created_at: datetime = Field(alias="createdAt")
15
59
  require_classification: bool = Field(alias="requireClassification")
@@ -1,3 +1,9 @@
1
+ """
2
+ Pydantic models for pipeline execution API responses.
3
+
4
+ This module defines the response models returned by pipeline execution endpoints,
5
+ including both synchronous and streaming response types.
6
+ """
1
7
  from typing import Any, AsyncIterator, Dict, Iterator
2
8
 
3
9
  from pydantic import BaseModel, ConfigDict, Field
@@ -6,24 +12,48 @@ from ..sse import SSEMessage
6
12
 
7
13
 
8
14
  class PipelineExecutionResponse(BaseModel):
15
+ """
16
+ Response model for standard pipeline execution requests.
17
+
18
+ This model represents the response when executing a pipeline in normal mode
19
+ (not debug mode and not streaming).
20
+ """
9
21
  result: str
10
22
  report: None
11
23
  is_backup_pipeline: bool = Field(alias="isBackupPipeline")
12
24
 
13
25
 
14
26
  class PipelineExecutionDebugResponse(BaseModel):
27
+ """
28
+ Response model for pipeline execution requests in debug mode.
29
+
30
+ This model includes additional debugging information in the report field
31
+ that provides insights into the pipeline's execution process.
32
+ """
15
33
  result: str
16
34
  report: Dict[str, Any]
17
35
  is_backup_pipeline: bool = Field(alias="isBackupPipeline")
18
36
 
19
37
 
20
38
  class PipelineExecutionStreamedResponse(BaseModel):
39
+ """
40
+ Response model for streaming pipeline execution requests (synchronous client).
41
+
42
+ This model contains an iterator that yields SSEMessage objects as they
43
+ are received from the streaming response.
44
+ """
21
45
  model_config = ConfigDict(arbitrary_types_allowed=True)
22
46
 
23
47
  stream: Iterator[SSEMessage]
24
48
 
25
49
 
26
50
  class PipelineExecutionAsyncStreamedResponse(BaseModel):
51
+ """
52
+ Response model for streaming pipeline execution requests (asynchronous client).
53
+
54
+ This model contains an async iterator that yields SSEMessage objects as they
55
+ are received from the streaming response.
56
+ """
27
57
  model_config = ConfigDict(arbitrary_types_allowed=True)
28
58
 
29
59
  stream: AsyncIterator[SSEMessage]
@@ -1,3 +1,10 @@
1
+ """
2
+ Server-Sent Event (SSE) message types for the Airia SDK.
3
+
4
+ This package contains all the message types that can be received via SSE streams
5
+ during pipeline execution, including agent lifecycle events, processing steps,
6
+ model streaming fragments, and tool execution updates.
7
+ """
1
8
  from .sse_messages import (
2
9
  SSEDict,
3
10
  SSEMessage,
@@ -1,3 +1,10 @@
1
+ """
2
+ Server-Sent Event (SSE) message types and models for the Airia API.
3
+
4
+ This module defines all possible SSE message types that can be received during
5
+ pipeline execution, including agent lifecycle events, processing steps, model
6
+ streaming, and tool execution messages.
7
+ """
1
8
  from datetime import datetime, time
2
9
  from enum import Enum
3
10
  from typing import Optional, Union
@@ -6,6 +13,12 @@ from pydantic import BaseModel, ConfigDict
6
13
 
7
14
 
8
15
  class MessageType(str, Enum):
16
+ """
17
+ Enumeration of all possible SSE message types from the Airia API.
18
+
19
+ These message types correspond to different events that occur during
20
+ pipeline execution, agent processing, and streaming responses.
21
+ """
9
22
  AGENT_PING = "AgentPingMessage"
10
23
  AGENT_START = "AgentStartMessage"
11
24
  AGENT_INPUT = "AgentInputMessage"
@@ -35,11 +48,23 @@ class MessageType(str, Enum):
35
48
 
36
49
 
37
50
  class BaseSSEMessage(BaseModel):
51
+ """
52
+ Base class for all Server-Sent Event (SSE) messages from the Airia API.
53
+
54
+ All SSE messages include a message_type field that identifies the specific
55
+ type of event being reported.
56
+ """
38
57
  model_config = ConfigDict(use_enum_values=True)
39
58
  message_type: MessageType
40
59
 
41
60
 
42
61
  class AgentPingMessage(BaseSSEMessage):
62
+ """
63
+ Ping message sent periodically to maintain connection health.
64
+
65
+ These messages help verify that the connection is still active during
66
+ long-running pipeline executions.
67
+ """
43
68
  message_type: MessageType = MessageType.AGENT_PING
44
69
  timestamp: datetime
45
70
 
@@ -48,19 +73,34 @@ class AgentPingMessage(BaseSSEMessage):
48
73
 
49
74
 
50
75
  class BaseAgentMessage(BaseSSEMessage):
76
+ """
77
+ Base class for messages related to agent execution.
78
+
79
+ All agent messages include identifiers for the specific agent
80
+ and execution session.
81
+ """
51
82
  agent_id: str
52
83
  execution_id: str
53
84
 
54
85
 
55
86
  class AgentStartMessage(BaseAgentMessage):
87
+ """
88
+ Message indicating that an agent has started processing.
89
+ """
56
90
  message_type: MessageType = MessageType.AGENT_START
57
91
 
58
92
 
59
93
  class AgentInputMessage(BaseAgentMessage):
94
+ """
95
+ Message indicating that an agent has received input to process.
96
+ """
60
97
  message_type: MessageType = MessageType.AGENT_INPUT
61
98
 
62
99
 
63
100
  class AgentEndMessage(BaseAgentMessage):
101
+ """
102
+ Message indicating that an agent has finished processing.
103
+ """
64
104
  message_type: MessageType = MessageType.AGENT_END
65
105
 
66
106
 
@@ -68,22 +108,42 @@ class AgentEndMessage(BaseAgentMessage):
68
108
 
69
109
 
70
110
  class BaseStepMessage(BaseAgentMessage):
111
+ """
112
+ Base class for messages related to individual processing steps within an agent.
113
+
114
+ Steps represent discrete operations or tasks that an agent performs
115
+ as part of its overall processing workflow.
116
+ """
71
117
  step_id: str
72
118
  step_type: str
73
119
  step_title: Optional[str] = None
74
120
 
75
121
 
76
122
  class AgentStepStartMessage(BaseStepMessage):
123
+ """
124
+ Message indicating that a processing step has started.
125
+ """
77
126
  message_type: MessageType = MessageType.AGENT_STEP_START
78
127
  start_time: datetime
79
128
 
80
129
 
81
130
  class AgentStepHaltMessage(BaseStepMessage):
131
+ """
132
+ Message indicating that a step has been halted pending approval.
133
+
134
+ This occurs when human approval is required before proceeding
135
+ with potentially sensitive or high-impact operations.
136
+ """
82
137
  message_type: MessageType = MessageType.AGENT_STEP_HALT
83
138
  approval_id: str
84
139
 
85
140
 
86
141
  class AgentStepEndMessage(BaseStepMessage):
142
+ """
143
+ Message indicating that a processing step has completed.
144
+
145
+ Includes timing information and the final status of the step.
146
+ """
87
147
  message_type: MessageType = MessageType.AGENT_STEP_END
88
148
  end_time: datetime
89
149
  duration: time
@@ -91,6 +151,9 @@ class AgentStepEndMessage(BaseStepMessage):
91
151
 
92
152
 
93
153
  class AgentOutputMessage(BaseStepMessage):
154
+ """
155
+ Message containing the output result from a completed step.
156
+ """
94
157
  message_type: MessageType = MessageType.AGENT_OUTPUT
95
158
  step_result: str
96
159
 
@@ -99,15 +162,33 @@ class AgentOutputMessage(BaseStepMessage):
99
162
 
100
163
 
101
164
  class BaseStatusMessage(BaseStepMessage):
165
+ """
166
+ Base class for status update messages within processing steps.
167
+
168
+ Status messages provide real-time updates about what operations
169
+ are being performed during step execution.
170
+ """
102
171
  pass
103
172
 
104
173
 
105
174
  class AgentAgentCardMessage(BaseStatusMessage):
175
+ """
176
+ Message indicating that an agent card step is being processed.
177
+
178
+ Agent cards represent interactive UI components or displays
179
+ that provide rich information to users during pipeline execution.
180
+ """
106
181
  message_type: MessageType = MessageType.AGENT_AGENT_CARD
107
182
  step_name: str
108
183
 
109
184
 
110
185
  class AgentDatasearchMessage(BaseStatusMessage):
186
+ """
187
+ Message indicating that data source search is being performed.
188
+
189
+ This message is sent when an agent is querying or searching
190
+ through configured data sources to retrieve relevant information.
191
+ """
111
192
  message_type: MessageType = MessageType.AGENT_DATASEARCH
112
193
  datastore_id: str
113
194
  datastore_type: str
@@ -115,21 +196,45 @@ class AgentDatasearchMessage(BaseStatusMessage):
115
196
 
116
197
 
117
198
  class AgentInvocationMessage(BaseStatusMessage):
199
+ """
200
+ Message indicating that another agent is being invoked.
201
+
202
+ This occurs when the current agent calls or delegates work
203
+ to another specialized agent in the pipeline.
204
+ """
118
205
  message_type: MessageType = MessageType.AGENT_INVOCATION
119
206
  agent_name: str
120
207
 
121
208
 
122
209
  class AgentModelMessage(BaseStatusMessage):
210
+ """
211
+ Message indicating that a language model is being called.
212
+
213
+ This message is sent when an agent begins interacting with
214
+ a language model for text generation or processing.
215
+ """
123
216
  message_type: MessageType = MessageType.AGENT_MODEL
124
217
  model_name: str
125
218
 
126
219
 
127
220
  class AgentPythonCodeMessage(BaseStatusMessage):
221
+ """
222
+ Message indicating that Python code execution is taking place.
223
+
224
+ This message is sent when an agent executes custom Python code
225
+ blocks as part of its processing workflow.
226
+ """
128
227
  message_type: MessageType = MessageType.AGENT_PYTHON_CODE
129
228
  step_name: str
130
229
 
131
230
 
132
231
  class AgentToolActionMessage(BaseStatusMessage):
232
+ """
233
+ Message indicating that a tool or external service is being called.
234
+
235
+ This message is sent when an agent invokes an external tool,
236
+ API, or service to perform a specific action or retrieve data.
237
+ """
133
238
  message_type: MessageType = MessageType.AGENT_TOOL_ACTION
134
239
  step_name: str
135
240
  tool_name: str
@@ -139,33 +244,57 @@ class AgentToolActionMessage(BaseStatusMessage):
139
244
 
140
245
 
141
246
  class BaseModelStreamMessage(BaseAgentMessage):
247
+ """
248
+ Base class for language model streaming messages.
249
+
250
+ Model streaming allows real-time display of text generation
251
+ as it occurs, providing better user experience for long responses.
252
+ """
142
253
  step_id: str
143
254
  stream_id: str
144
255
 
145
256
 
146
257
  class AgentModelStreamStartMessage(BaseModelStreamMessage):
258
+ """
259
+ Message indicating that model text streaming has begun.
260
+ """
147
261
  message_type: MessageType = MessageType.AGENT_MODEL_STREAM_START
148
262
  model_name: str
149
263
 
150
264
 
151
265
  class AgentModelStreamErrorMessage(BaseModelStreamMessage):
266
+ """
267
+ Message indicating that an error occurred during model streaming.
268
+ """
152
269
  message_type: MessageType = MessageType.AGENT_MODEL_STREAM_ERROR
153
270
  error_message: str
154
271
 
155
272
 
156
273
  class AgentModelStreamFragmentMessage(BaseModelStreamMessage):
274
+ """
275
+ Fragment of streaming text content from a language model.
276
+
277
+ These messages contain individual chunks of text as they are generated
278
+ by the model, allowing for real-time display of results.
279
+ """
157
280
  message_type: MessageType = MessageType.AGENT_MODEL_STREAM_FRAGMENT
158
281
  index: int
159
282
  content: Optional[str] = None
160
283
 
161
284
 
162
285
  class AgentModelStreamEndMessage(BaseModelStreamMessage):
286
+ """
287
+ Message indicating that model text streaming has completed.
288
+ """
163
289
  message_type: MessageType = MessageType.AGENT_MODEL_STREAM_END
164
290
  content_id: str
165
291
  duration: Optional[float] = None
166
292
 
167
293
 
168
294
  class AgentModelStreamUsageMessage(BaseModelStreamMessage):
295
+ """
296
+ Message containing token usage and cost information for model calls.
297
+ """
169
298
  message_type: MessageType = MessageType.AGENT_MODEL_STREAM_USAGE
170
299
  token: Optional[int] = None
171
300
  tokens_cost: Optional[float] = None
@@ -175,27 +304,48 @@ class AgentModelStreamUsageMessage(BaseModelStreamMessage):
175
304
 
176
305
 
177
306
  class BaseAgentAgentCardStreamMessage(BaseAgentMessage):
307
+ """
308
+ Base class for agent card streaming messages.
309
+
310
+ Agent card streaming allows real-time updates to interactive
311
+ UI components during their generation or processing.
312
+ """
178
313
  step_id: str
179
314
  stream_id: str
180
315
 
181
316
 
182
317
  class AgentAgentCardStreamStartMessage(BaseAgentAgentCardStreamMessage):
318
+ """
319
+ Message indicating that agent card streaming has begun.
320
+ """
183
321
  message_type: MessageType = MessageType.AGENT_AGENT_CARD_STREAM_START
184
322
  content: Optional[str] = None
185
323
 
186
324
 
187
325
  class AgentAgentCardStreamErrorMessage(BaseAgentAgentCardStreamMessage):
326
+ """
327
+ Message indicating that an error occurred during agent card streaming.
328
+ """
188
329
  message_type: MessageType = MessageType.AGENT_AGENT_CARD_STREAM_ERROR
189
330
  error_message: str
190
331
 
191
332
 
192
333
  class AgentAgentCardStreamFragmentMessage(BaseAgentAgentCardStreamMessage):
334
+ """
335
+ Fragment of streaming agent card content.
336
+
337
+ These messages contain individual chunks of agent card data
338
+ as they are generated, allowing for real-time UI updates.
339
+ """
193
340
  message_type: MessageType = MessageType.AGENT_AGENT_CARD_STREAM_FRAGMENT
194
341
  index: int
195
342
  content: Optional[str]
196
343
 
197
344
 
198
345
  class AgentAgentCardStreamEndMessage(BaseAgentAgentCardStreamMessage):
346
+ """
347
+ Message indicating that agent card streaming has completed.
348
+ """
199
349
  message_type: MessageType = MessageType.AGENT_AGENT_CARD_STREAM_END
200
350
  content: Optional[str] = None
201
351
 
@@ -204,15 +354,33 @@ class AgentAgentCardStreamEndMessage(BaseAgentAgentCardStreamMessage):
204
354
 
205
355
 
206
356
  class BaseAgentToolMessage(BaseStepMessage):
357
+ """
358
+ Base class for tool execution messages.
359
+
360
+ Tool messages track the lifecycle of external tool or service
361
+ calls made by agents during pipeline execution.
362
+ """
207
363
  id: str
208
364
  name: str
209
365
 
210
366
 
211
367
  class AgentToolRequestMessage(BaseAgentToolMessage):
368
+ """
369
+ Message indicating that a tool request has been initiated.
370
+
371
+ This message is sent when an agent begins calling an external
372
+ tool or service to perform a specific operation.
373
+ """
212
374
  message_type: MessageType = MessageType.AGENT_TOOL_REQUEST
213
375
 
214
376
 
215
377
  class AgentToolResponseMessage(BaseAgentToolMessage):
378
+ """
379
+ Message indicating that a tool request has completed.
380
+
381
+ This message contains the results and timing information
382
+ from a completed tool or service call.
383
+ """
216
384
  message_type: MessageType = MessageType.AGENT_TOOL_RESPONSE
217
385
  duration: time
218
386
  success: bool
@@ -246,6 +414,7 @@ SSEMessage = Union[
246
414
  AgentToolRequestMessage,
247
415
  AgentToolResponseMessage,
248
416
  ]
417
+ """Union type representing all possible SSE message types from the Airia API."""
249
418
 
250
419
  SSEDict = {
251
420
  MessageType.AGENT_PING.value: AgentPingMessage,
@@ -274,3 +443,9 @@ SSEDict = {
274
443
  MessageType.AGENT_TOOL_REQUEST.value: AgentToolRequestMessage,
275
444
  MessageType.AGENT_TOOL_RESPONSE.value: AgentToolResponseMessage,
276
445
  }
446
+ """
447
+ Mapping from message type strings to their corresponding Pydantic model classes.
448
+
449
+ This dictionary is used by the SSE parser to instantiate the correct message
450
+ type based on the 'event' field in incoming SSE data.
451
+ """
airia/utils/sse_parser.py CHANGED
@@ -1,3 +1,10 @@
1
+ """
2
+ Server-Sent Events (SSE) stream parsing utilities.
3
+
4
+ This module provides functions for parsing SSE streams from both synchronous and
5
+ asynchronous HTTP responses. It handles the SSE protocol format and converts
6
+ raw stream data into typed message objects.
7
+ """
1
8
  import json
2
9
  import re
3
10
  from typing import AsyncIterable, AsyncIterator, Iterable, Iterator
@@ -5,19 +12,36 @@ from typing import AsyncIterable, AsyncIterator, Iterable, Iterator
5
12
  from ..types.sse import SSEDict, SSEMessage
6
13
 
7
14
 
8
- def _to_snake_case(name: str):
15
+ def _to_snake_case(name: str) -> str:
16
+ """
17
+ Convert camelCase or PascalCase string to snake_case.
18
+
19
+ Args:
20
+ name: The string to convert to snake_case
21
+
22
+ Returns:
23
+ The string converted to snake_case format
24
+ """
9
25
  return re.sub(r"(?<!^)(?=[A-Z])", "_", name).lower()
10
26
 
11
27
 
12
28
  def parse_sse_stream_chunked(stream_chunks: Iterable[bytes]) -> Iterator[SSEMessage]:
13
29
  """
14
- Parse SSE stream from an iterable of chunks (e.g., from a streaming response).
30
+ Parse Server-Sent Events (SSE) stream from an iterable of byte chunks.
31
+
32
+ This function processes streaming data from HTTP responses that follow the SSE protocol,
33
+ parsing event blocks and converting them into typed SSE message objects. It handles
34
+ incomplete chunks by buffering data until complete events are received.
15
35
 
16
36
  Args:
17
- stream_chunks: Iterable of string chunks
37
+ stream_chunks: An iterable of byte chunks from an HTTP streaming response
18
38
 
19
39
  Yields:
20
- dict: Dictionary containing 'event' and 'data' keys
40
+ SSEMessage: Typed SSE message objects corresponding to the parsed events
41
+
42
+ Note:
43
+ Events are expected to follow the SSE format with 'event:' and 'data:' lines,
44
+ terminated by double newlines (\n\n). The data portion should contain valid JSON.
21
45
  """
22
46
  buffer = ""
23
47
 
@@ -53,13 +77,21 @@ async def async_parse_sse_stream_chunked(
53
77
  stream_chunks: AsyncIterable[bytes],
54
78
  ) -> AsyncIterator[SSEMessage]:
55
79
  """
56
- Parse SSE stream from an iterable of chunks (e.g., from a streaming response).
80
+ Asynchronously parse Server-Sent Events (SSE) stream from an async iterable of byte chunks.
81
+
82
+ This is the async version of parse_sse_stream_chunked, designed for use with
83
+ asynchronous HTTP clients. It processes streaming data that follows the SSE protocol,
84
+ parsing event blocks and converting them into typed SSE message objects.
57
85
 
58
86
  Args:
59
- stream_chunks: Iterable of string chunks
87
+ stream_chunks: An async iterable of byte chunks from an HTTP streaming response
60
88
 
61
89
  Yields:
62
- dict: Dictionary containing 'event' and 'data' keys
90
+ SSEMessage: Typed SSE message objects corresponding to the parsed events
91
+
92
+ Note:
93
+ Events are expected to follow the SSE format with 'event:' and 'data:' lines,
94
+ terminated by double newlines (\n\n). The data portion should contain valid JSON.
63
95
  """
64
96
  buffer = ""
65
97