autocoder-nano 0.1.33__py3-none-any.whl → 0.1.34__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,8 +1,8 @@
1
1
  import dataclasses
2
2
  from enum import Enum
3
- from typing import List, Dict, Any, Optional, Union, Tuple, Set
3
+ from typing import List, Dict, Any, Optional, Union, Tuple, Set, Callable
4
4
 
5
- from pydantic import BaseModel, Field
5
+ from pydantic import BaseModel, Field, SkipValidation
6
6
 
7
7
 
8
8
  class AutoCoderArgs(BaseModel):
@@ -241,6 +241,23 @@ class Tag(BaseModel):
241
241
  end_tag: str
242
242
 
243
243
 
244
+ class FileSystemModel(BaseModel):
245
+ project_root: str
246
+ get_all_file_names_in_project: SkipValidation[Callable]
247
+ get_all_file_in_project: SkipValidation[Callable]
248
+ get_all_dir_names_in_project: SkipValidation[Callable]
249
+ get_all_file_in_project_with_dot: SkipValidation[Callable]
250
+ get_symbol_list: SkipValidation[Callable]
251
+
252
+
253
+ class MemoryConfig(BaseModel):
254
+ get_memory_func: SkipValidation[Callable]
255
+ save_memory_func: SkipValidation[Callable]
256
+
257
+ class Config:
258
+ arbitrary_types_allowed = True
259
+
260
+
244
261
  class SymbolItem(BaseModel):
245
262
  symbol_name: str
246
263
  symbol_type: SymbolType
@@ -0,0 +1,616 @@
1
+ import os
2
+ from typing import List, Any
3
+
4
+ from prompt_toolkit.completion import Completer, Completion
5
+
6
+ from autocoder_nano.llm_types import Tag, AutoCoderArgs, FileSystemModel, MemoryConfig
7
+
8
+ COMMANDS = {
9
+ "/add_files": {
10
+ "/group": {"/add": "", "/drop": "", "/reset": ""},
11
+ "/refresh": "",
12
+ },
13
+ "/remove_files": {"/all": ""},
14
+ "/coding": {"/apply": ""},
15
+ "/chat": {"/history": "", "/new": "", "/review": ""},
16
+ "/models": {"/add_model": "", "/remove": "", "/list": "", "/check": ""},
17
+ "/help": {
18
+ "/add_files": "",
19
+ "/remove_files": "",
20
+ "/chat": "",
21
+ "/coding": "",
22
+ "/commit": "",
23
+ "/conf": "",
24
+ "/mode": "",
25
+ "/models": ""
26
+ },
27
+ "/exclude_files": {"/list": "", "/drop": ""},
28
+ "/exclude_dirs": {},
29
+ "/rules": {"/list": "", "/show": "", "/remove": "", "/analyze": "", "/commit": ""}
30
+ }
31
+
32
+
33
+ class CommandTextParser:
34
+ def __init__(self, text: str, command: str):
35
+ self.text = text
36
+ self.pos = -1
37
+ self.len = len(text)
38
+ self.is_extracted = False
39
+ self.current_word_start_pos = 0
40
+ self.current_word_end_pos = 0
41
+ self.in_current_sub_command = ""
42
+ self.completions = []
43
+ self.command = command
44
+ self.current_hiararchy = COMMANDS[command]
45
+ self.sub_commands = []
46
+ self.tags = []
47
+
48
+ def first_sub_command(self):
49
+ if len(self.sub_commands) == 0:
50
+ return None
51
+ return self.sub_commands[0]
52
+
53
+ def last_sub_command(self):
54
+ if len(self.sub_commands) == 0:
55
+ return None
56
+ return self.sub_commands[-1]
57
+
58
+ def peek(self):
59
+ if self.pos + 1 < self.len:
60
+ return self.text[self.pos + 1]
61
+ return None
62
+
63
+ def peek2(self):
64
+ if self.pos + 2 < self.len:
65
+ return self.text[self.pos + 2]
66
+ return None
67
+
68
+ def peek3(self):
69
+ if self.pos + 3 < self.len:
70
+ return self.text[self.pos + 3]
71
+ return None
72
+
73
+ def next(self):
74
+ if self.pos < self.len - 1:
75
+ self.pos += 1
76
+ char = self.text[self.pos]
77
+ return char
78
+ return None
79
+
80
+ def consume_blank(self):
81
+ while self.peek() == "\n" or self.peek() == " " or self.peek() == "\t" or self.peek() == "\r":
82
+ self.next()
83
+
84
+ def is_blank(self) -> bool:
85
+ return self.peek() == "\n" or self.peek() == " " or self.peek() == "\t" or self.peek() == "\r"
86
+
87
+ def is_sub_command(self) -> bool | None:
88
+ backup_pos = self.pos
89
+ self.consume_blank()
90
+ try:
91
+ if self.peek() == "/":
92
+ current_sub_command = ""
93
+ while self.peek() is not None and self.peek() != " " and self.peek() != "\n":
94
+ current_sub_command += self.next()
95
+
96
+ if current_sub_command.count("/") > 1:
97
+ self.pos = backup_pos
98
+ return False
99
+ return True
100
+ return False
101
+ finally:
102
+ self.pos = backup_pos
103
+
104
+ def consume_sub_command(self) -> str:
105
+ # backup_pos = self.pos
106
+ self.consume_blank()
107
+ current_sub_command = ""
108
+ while self.peek() is not None and self.peek() != " " and self.peek() != "\n":
109
+ current_sub_command += self.next()
110
+
111
+ if self.peek() is None:
112
+ self.is_extracted = True
113
+ self.current_word_end_pos = self.pos + 1
114
+ self.current_word_start_pos = self.current_word_end_pos - len(
115
+ current_sub_command
116
+ )
117
+ self.in_current_sub_command = current_sub_command
118
+ else:
119
+ if current_sub_command in self.current_hiararchy:
120
+ self.current_hiararchy = self.current_hiararchy[current_sub_command]
121
+ self.sub_commands.append(current_sub_command)
122
+
123
+ return current_sub_command
124
+
125
+ def consume_command_value(self):
126
+ current_word = ""
127
+ while self.peek() is not None:
128
+ v = self.next()
129
+ if v == " ":
130
+ current_word = ""
131
+ else:
132
+ current_word += v
133
+ self.is_extracted = True
134
+ self.current_word_end_pos = self.pos + 1
135
+ self.current_word_start_pos = self.current_word_end_pos - len(current_word)
136
+
137
+ def previous(self):
138
+ if self.pos > 1:
139
+ return self.text[self.pos - 1]
140
+ return None
141
+
142
+ def is_start_tag(self) -> bool | None:
143
+ backup_pos = self.pos
144
+ tag = ""
145
+ try:
146
+ if self.peek() == "<" and self.peek2() != "/":
147
+ while (
148
+ self.peek() is not None
149
+ and self.peek() != ">"
150
+ and not self.is_blank()
151
+ ):
152
+ tag += self.next()
153
+ if self.peek() == ">":
154
+ tag += self.next()
155
+ return True
156
+ else:
157
+ return False
158
+ return False
159
+ finally:
160
+ self.pos = backup_pos
161
+
162
+ def consume_tag(self):
163
+ start_tag = ""
164
+ content = ""
165
+ end_tag = ""
166
+
167
+ # consume start tag
168
+ self.current_word_start_pos = self.pos + 1
169
+ while self.peek() is not None and self.peek() != ">" and not self.is_blank():
170
+ start_tag += self.next()
171
+ if self.peek() == ">":
172
+ start_tag += self.next()
173
+ self.current_word_end_pos = self.pos + 1
174
+ tag = Tag(start_tag=start_tag, content=content, end_tag=end_tag)
175
+ self.tags.append(tag)
176
+
177
+ # consume content
178
+ self.current_word_start_pos = self.pos + 1
179
+ while self.peek() is not None and not (
180
+ self.peek() == "<" and self.peek2() == "/"
181
+ ):
182
+ content += self.next()
183
+
184
+ tag.content = content
185
+ self.current_word_end_pos = self.pos + 1
186
+
187
+ # consume end tag
188
+ self.current_word_start_pos = self.pos + 1
189
+ if self.peek() == "<" and self.peek2() == "/":
190
+ while (
191
+ self.peek() is not None and self.peek() != ">" and not self.is_blank()
192
+ ):
193
+ end_tag += self.next()
194
+ if self.peek() == ">":
195
+ end_tag += self.next()
196
+ tag.end_tag = end_tag
197
+ self.current_word_end_pos = self.pos + 1
198
+
199
+ # check is finished
200
+ if self.peek() is None:
201
+ self.is_extracted = True
202
+
203
+ def consume_coding_value(self):
204
+ current_word = ""
205
+ while self.peek() is not None and not self.is_start_tag():
206
+ v = self.next()
207
+ if v == " ":
208
+ current_word = ""
209
+ else:
210
+ current_word += v
211
+ if self.peek() is None:
212
+ self.is_extracted = True
213
+
214
+ self.current_word_end_pos = self.pos + 1
215
+ self.current_word_start_pos = self.current_word_end_pos - len(current_word)
216
+
217
+ def current_word(self) -> str:
218
+ return self.text[self.current_word_start_pos: self.current_word_end_pos]
219
+
220
+ def get_current_word(self) -> str:
221
+ return self.current_word()
222
+
223
+ def get_sub_commands(self) -> list[str]:
224
+ if self.get_current_word() and not self.get_current_word().startswith("/"):
225
+ return []
226
+
227
+ if isinstance(self.current_hiararchy, str):
228
+ return []
229
+
230
+ return [item for item in list(self.current_hiararchy.keys()) if item]
231
+
232
+ def add_files(self):
233
+ """
234
+ for exmaple:
235
+ /add_files file1 file2 file3
236
+ /add_files /group/abc/cbd /group/abc/bc2
237
+ /add_files /group1 /add xxxxx
238
+ /add_files /group
239
+ /add_files /group /add <groupname>
240
+ /add_files /group /drop <groupname>
241
+ /add_files /group <groupname>,<groupname>
242
+ /add_files /refresh
243
+ """
244
+ while True:
245
+ if self.pos == self.len - 1:
246
+ break
247
+ elif self.is_extracted:
248
+ break
249
+ elif self.is_sub_command():
250
+ self.consume_sub_command()
251
+ else:
252
+ self.consume_command_value()
253
+ return self
254
+
255
+ def coding(self):
256
+ while True:
257
+ if self.pos == self.len - 1:
258
+ break
259
+ elif self.is_extracted:
260
+ break
261
+ elif self.is_sub_command():
262
+ self.consume_sub_command()
263
+ elif self.is_start_tag():
264
+ self.consume_tag()
265
+ else:
266
+ self.consume_coding_value()
267
+
268
+
269
+ class CommandCompleter(Completer):
270
+ def __init__(self, commands: List[str], file_system_model: FileSystemModel, memory_model: MemoryConfig):
271
+ self.commands = commands
272
+ self.file_system_model = file_system_model
273
+ self.memory_model = memory_model
274
+
275
+ # self.all_file_names = get_all_file_names_in_project()
276
+ # self.all_files = get_all_file_in_project()
277
+ # self.all_dir_names = get_all_dir_names_in_project()
278
+ # self.all_files_with_dot = get_all_file_in_project_with_dot()
279
+ # self.symbol_list = get_symbol_list()
280
+ self.all_file_names: List[str] = []
281
+ self.all_files: List[str] = []
282
+ self.all_dir_names: List[str] = []
283
+ self.all_files_with_dot: List[str] = []
284
+ self.symbol_list: List[Any] = [] # Use Any for SymbolItem structure from runner
285
+ self.current_file_names: List[str] = []
286
+
287
+ self.refresh_files()
288
+
289
+ def get_completions(self, document, complete_event):
290
+ text = document.text_before_cursor
291
+ words = text.split()
292
+
293
+ if len(words) > 0:
294
+ if words[0] == "/mode":
295
+ left_word = text[len("/mode"):]
296
+ for mode in ["normal", "auto_detect"]:
297
+ if mode.startswith(left_word.strip()):
298
+ yield Completion(mode, start_position=-len(left_word.strip()))
299
+
300
+ if words[0] == "/add_files":
301
+ new_text = text[len("/add_files"):]
302
+ parser = CommandTextParser(new_text, words[0])
303
+ parser.add_files()
304
+ current_word = parser.current_word()
305
+
306
+ if parser.last_sub_command() == "/refresh":
307
+ return
308
+
309
+ for command in parser.get_sub_commands():
310
+ if command.startswith(current_word):
311
+ yield Completion(command, start_position=-len(current_word))
312
+
313
+ if parser.first_sub_command() == "/group" and (
314
+ parser.last_sub_command() == "/group"
315
+ or parser.last_sub_command() == "/drop"
316
+ ):
317
+ group_names = list(self.memory_model.get_memory_func().get("current_files", {}).get("groups", {}).keys())
318
+ if "," in current_word:
319
+ current_word = current_word.split(",")[-1]
320
+
321
+ for group_name in group_names:
322
+ if group_name.startswith(current_word):
323
+ yield Completion(
324
+ group_name, start_position=-len(current_word)
325
+ )
326
+
327
+ if parser.first_sub_command() != "/group":
328
+ if current_word and current_word.startswith("."):
329
+ for file_name in self.all_files_with_dot:
330
+ if file_name.startswith(current_word):
331
+ yield Completion(file_name, start_position=-len(current_word))
332
+ else:
333
+ for file_name in self.all_file_names:
334
+ if file_name.startswith(current_word):
335
+ yield Completion(file_name, start_position=-len(current_word))
336
+ for file_name in self.all_files:
337
+ if current_word and current_word in file_name:
338
+ yield Completion(file_name, start_position=-len(current_word))
339
+
340
+ elif words[0] in ["/chat", "/coding"]:
341
+ image_extensions = (
342
+ ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff", ".tif", ".webp", ".svg", ".ico",
343
+ ".heic", ".heif", ".raw", ".cr2", ".nef", ".arw", ".dng", ".orf", ".rw2", ".pef",
344
+ ".srw", ".eps", ".ai", ".psd", ".xcf",
345
+ )
346
+ new_text = text[len(words[0]):]
347
+ parser = CommandTextParser(new_text, words[0])
348
+
349
+ parser.coding()
350
+ current_word = parser.current_word()
351
+
352
+ if len(new_text.strip()) == 0 or new_text.strip() == "/":
353
+ for command in parser.get_sub_commands():
354
+ if command.startswith(current_word):
355
+ yield Completion(command, start_position=-len(current_word))
356
+
357
+ all_tags = parser.tags
358
+
359
+ if current_word.startswith("@"):
360
+ name = current_word[1:]
361
+ target_set = set()
362
+
363
+ for file_name in self.current_file_names:
364
+ base_file_name = os.path.basename(file_name)
365
+ if name in base_file_name:
366
+ target_set.add(base_file_name)
367
+ path_parts = file_name.split(os.sep)
368
+ display_name = (
369
+ os.sep.join(path_parts[-3:])
370
+ if len(path_parts) > 3
371
+ else file_name
372
+ )
373
+ relative_path = os.path.relpath(
374
+ file_name, self.file_system_model.project_root)
375
+ yield Completion(
376
+ relative_path,
377
+ start_position=-len(name),
378
+ display=f"{display_name} (in active files)",
379
+ )
380
+
381
+ for file_name in self.all_file_names:
382
+ if file_name.startswith(name) and file_name not in target_set:
383
+ target_set.add(file_name)
384
+
385
+ path_parts = file_name.split(os.sep)
386
+ display_name = (
387
+ os.sep.join(path_parts[-3:])
388
+ if len(path_parts) > 3
389
+ else file_name
390
+ )
391
+ relative_path = os.path.relpath(
392
+ file_name, self.file_system_model.project_root)
393
+
394
+ yield Completion(
395
+ relative_path,
396
+ start_position=-len(name),
397
+ display=f"{display_name}",
398
+ )
399
+
400
+ for file_name in self.all_files:
401
+ if name in file_name and file_name not in target_set:
402
+ path_parts = file_name.split(os.sep)
403
+ display_name = (
404
+ os.sep.join(path_parts[-3:])
405
+ if len(path_parts) > 3
406
+ else file_name
407
+ )
408
+ relative_path = os.path.relpath(
409
+ file_name, self.file_system_model.project_root)
410
+ yield Completion(
411
+ relative_path,
412
+ start_position=-len(name),
413
+ display=f"{display_name}",
414
+ )
415
+
416
+ if current_word.startswith("@@"):
417
+ name = current_word[2:]
418
+ for symbol in self.symbol_list:
419
+ if name in symbol.symbol_name:
420
+ file_name = symbol.file_name
421
+ path_parts = file_name.split(os.sep)
422
+ display_name = (
423
+ os.sep.join(path_parts[-3:])
424
+ if len(path_parts) > 3
425
+ else symbol.symbol_name
426
+ )
427
+ relative_path = os.path.relpath(
428
+ file_name, self.file_system_model.project_root)
429
+ yield Completion(
430
+ f"{symbol.symbol_name}(location: {relative_path})",
431
+ start_position=-len(name),
432
+ display=f"{symbol.symbol_name} ({display_name}/{symbol.symbol_type})",
433
+ )
434
+
435
+ tags = [tag for tag in parser.tags]
436
+
437
+ if current_word.startswith("<"):
438
+ name = current_word[1:]
439
+ for tag in ["<img>", "</img>"]:
440
+ if all_tags and all_tags[-1].start_tag == "<img>":
441
+ if tag.startswith(name):
442
+ yield Completion(
443
+ "</img>", start_position=-len(current_word)
444
+ )
445
+ elif tag.startswith(name):
446
+ yield Completion(tag, start_position=-len(current_word))
447
+
448
+ if tags and tags[-1].start_tag == "<img>" and tags[-1].end_tag == "":
449
+ raw_file_name = tags[0].content
450
+ file_name = raw_file_name.strip()
451
+ parent_dir = os.path.dirname(file_name)
452
+ file_basename = os.path.basename(file_name)
453
+ search_dir = parent_dir if parent_dir else "."
454
+ for root, dirs, files in os.walk(search_dir):
455
+ # 只处理直接子目录
456
+ if root != search_dir:
457
+ continue
458
+
459
+ # 补全子目录
460
+ for _dir in dirs:
461
+ full_path = os.path.join(root, _dir)
462
+ if full_path.startswith(file_name):
463
+ relative_path = os.path.relpath(full_path, search_dir)
464
+ yield Completion(relative_path, start_position=-len(file_basename))
465
+
466
+ # 补全文件
467
+ for file in files:
468
+ if file.lower().endswith(
469
+ image_extensions
470
+ ) and file.startswith(file_basename):
471
+ full_path = os.path.join(root, file)
472
+ relative_path = os.path.relpath(full_path, search_dir)
473
+ yield Completion(
474
+ relative_path,
475
+ start_position=-len(file_basename),
476
+ )
477
+
478
+ # 只处理一层子目录,然后退出循环
479
+ break
480
+
481
+ elif words[0] == "/remove_files":
482
+ new_words = text[len("/remove_files"):].strip().split(",")
483
+
484
+ is_at_space = text[-1] == " "
485
+ last_word = new_words[-2] if len(new_words) > 1 else ""
486
+ current_word = new_words[-1] if new_words else ""
487
+
488
+ if is_at_space:
489
+ last_word = current_word
490
+ current_word = ""
491
+
492
+ # /remove_files /all [cursor] or /remove_files /all p[cursor]
493
+ if not last_word and not current_word:
494
+ if "/all".startswith(current_word):
495
+ yield Completion("/all", start_position=-len(current_word))
496
+ for file_name in self.current_file_names:
497
+ yield Completion(file_name, start_position=-len(current_word))
498
+
499
+ # /remove_files /a[cursor] or /remove_files p[cursor]
500
+ if current_word:
501
+ if "/all".startswith(current_word):
502
+ yield Completion("/all", start_position=-len(current_word))
503
+ for file_name in self.current_file_names:
504
+ if current_word and current_word in file_name:
505
+ yield Completion(
506
+ file_name, start_position=-len(current_word)
507
+ )
508
+
509
+ elif words[0] == "/exclude_dirs":
510
+ new_words = text[len("/exclude_dirs"):].strip().split(",")
511
+ current_word = new_words[-1]
512
+
513
+ for file_name in self.all_dir_names:
514
+ if current_word and current_word in file_name:
515
+ yield Completion(file_name, start_position=-len(current_word))
516
+
517
+ elif words[0] == "/exclude_files":
518
+ new_text = text[len("/exclude_files"):]
519
+ parser = CommandTextParser(new_text, words[0])
520
+ parser.add_files()
521
+ current_word = parser.current_word()
522
+ for command in parser.get_sub_commands():
523
+ if command.startswith(current_word):
524
+ yield Completion(command, start_position=-len(current_word))
525
+
526
+ elif words[0] == "/models":
527
+ new_text = text[len("/models"):]
528
+ parser = CommandTextParser(new_text, words[0])
529
+ parser.add_files()
530
+ current_word = parser.current_word()
531
+ for command in parser.get_sub_commands():
532
+ if command.startswith(current_word):
533
+ yield Completion(command, start_position=-len(current_word))
534
+
535
+ elif words[0] == "/help":
536
+ new_text = text[len("/help"):]
537
+ parser = CommandTextParser(new_text, words[0])
538
+ parser.add_files()
539
+ current_word = parser.current_word()
540
+ for command in parser.get_sub_commands():
541
+ if command.startswith(current_word):
542
+ yield Completion(command, start_position=-len(current_word))
543
+
544
+ elif words[0] == "/rules":
545
+ new_text = text[len("/rules"):]
546
+ parser = CommandTextParser(new_text, words[0])
547
+ parser.add_files()
548
+ current_word = parser.current_word()
549
+ for command in parser.get_sub_commands():
550
+ if command.startswith(current_word):
551
+ yield Completion(command, start_position=-len(current_word))
552
+
553
+ elif words[0] == "/conf":
554
+ new_words = text[len("/conf"):].strip().split()
555
+ is_at_space = text[-1] == " "
556
+ last_word = new_words[-2] if len(new_words) > 1 else ""
557
+ current_word = new_words[-1] if new_words else ""
558
+ completions = []
559
+
560
+ if is_at_space:
561
+ last_word = current_word
562
+ current_word = ""
563
+
564
+ # /conf /drop [curor] or /conf /drop p[cursor]
565
+ if last_word == "/drop":
566
+ completions = [
567
+ field_name
568
+ for field_name in self.memory_model.get_memory_func().get("conf", {}).keys()
569
+ if field_name.startswith(current_word)
570
+ ]
571
+ # /conf [curosr]
572
+ elif not last_word and not current_word:
573
+ completions = [
574
+ "/drop"] if "/drop".startswith(current_word) else []
575
+ completions += [
576
+ field_name + ":"
577
+ for field_name in AutoCoderArgs.model_fields.keys()
578
+ if field_name.startswith(current_word)
579
+ ]
580
+ # /conf p[cursor]
581
+ elif not last_word and current_word:
582
+ completions = [
583
+ "/drop"] if "/drop".startswith(current_word) else []
584
+ completions += [
585
+ field_name + ":"
586
+ for field_name in AutoCoderArgs.model_fields.keys()
587
+ if field_name.startswith(current_word)
588
+ ]
589
+
590
+ for completion in completions:
591
+ yield Completion(completion, start_position=-len(current_word))
592
+
593
+ else:
594
+ for command in self.commands:
595
+ if command.startswith(text):
596
+ yield Completion(command, start_position=-len(text))
597
+ else:
598
+ for command in self.commands:
599
+ if command.startswith(text):
600
+ yield Completion(command, start_position=-len(text))
601
+
602
+ def update_current_files(self, files):
603
+ self.current_file_names = [f for f in files]
604
+ # self.current_file_names = self.memory_model.get_memory_func().get("current_files", {}).get("files", [])
605
+
606
+ def refresh_files(self):
607
+ # self.all_file_names = get_all_file_names_in_project()
608
+ # self.all_files = get_all_file_in_project()
609
+ # self.all_dir_names = get_all_dir_names_in_project()
610
+ # self.all_files_with_dot = get_all_file_in_project_with_dot()
611
+ # self.symbol_list = get_symbol_list()
612
+ self.all_file_names = self.file_system_model.get_all_file_names_in_project()
613
+ self.all_files = self.file_system_model.get_all_file_in_project()
614
+ self.all_dir_names = self.file_system_model.get_all_dir_names_in_project()
615
+ self.all_files_with_dot = self.file_system_model.get_all_file_in_project_with_dot()
616
+ self.symbol_list = self.file_system_model.get_symbol_list()
autocoder_nano/version.py CHANGED
@@ -1,3 +1,3 @@
1
- __version__ = "0.1.33"
1
+ __version__ = "0.1.34"
2
2
  __author__ = "moofs"
3
3
  __license__ = "Apache License 2.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: autocoder_nano
3
- Version: 0.1.33
3
+ Version: 0.1.34
4
4
  Summary: AutoCoder Nano
5
5
  Author: moofs
6
6
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence