janus-llm 3.3.2__py3-none-any.whl → 3.4.0__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.
janus/__init__.py CHANGED
@@ -5,7 +5,7 @@ from langchain_core._api.deprecation import LangChainDeprecationWarning
5
5
  from janus.converter.translate import Translator
6
6
  from janus.metrics import * # noqa: F403
7
7
 
8
- __version__ = "3.3.2"
8
+ __version__ = "3.4.0"
9
9
 
10
10
  # Ignoring a deprecation warning from langchain_core that I can't seem to hunt down
11
11
  warnings.filterwarnings("ignore", category=LangChainDeprecationWarning)
janus/cli.py CHANGED
@@ -200,6 +200,14 @@ def translate(
200
200
  help="Whether to overwrite existing files in the output directory",
201
201
  ),
202
202
  ] = False,
203
+ skip_context: Annotated[
204
+ bool,
205
+ typer.Option(
206
+ "--skip-context",
207
+ help="Prompts will include any context information associated with source"
208
+ " code blocks, unless this option is specified",
209
+ ),
210
+ ] = False,
203
211
  temp: Annotated[
204
212
  float,
205
213
  typer.Option("--temperature", "-T", help="Sampling temperature.", min=0, max=2),
@@ -265,6 +273,7 @@ def translate(
265
273
  db_path=db_loc,
266
274
  db_config=collections_config,
267
275
  splitter_type=splitter_type,
276
+ skip_context=skip_context,
268
277
  )
269
278
  translator.translate(input_dir, output_dir, overwrite, collection)
270
279
 
@@ -322,6 +331,14 @@ def document(
322
331
  help="Whether to overwrite existing files in the output directory",
323
332
  ),
324
333
  ] = False,
334
+ skip_context: Annotated[
335
+ bool,
336
+ typer.Option(
337
+ "--skip-context",
338
+ help="Prompts will include any context information associated with source"
339
+ " code blocks, unless this option is specified",
340
+ ),
341
+ ] = False,
325
342
  doc_mode: Annotated[
326
343
  str,
327
344
  typer.Option(
@@ -390,6 +407,7 @@ def document(
390
407
  db_path=db_loc,
391
408
  db_config=collections_config,
392
409
  splitter_type=splitter_type,
410
+ skip_context=skip_context,
393
411
  )
394
412
  if doc_mode == "madlibs":
395
413
  documenter = MadLibsDocumenter(
@@ -458,6 +476,14 @@ def diagram(
458
476
  help="Whether to overwrite existing files in the output directory",
459
477
  ),
460
478
  ] = False,
479
+ skip_context: Annotated[
480
+ bool,
481
+ typer.Option(
482
+ "--skip-context",
483
+ help="Prompts will include any context information associated with source"
484
+ " code blocks, unless this option is specified",
485
+ ),
486
+ ] = False,
461
487
  temperature: Annotated[
462
488
  float,
463
489
  typer.Option("--temperature", "-t", help="Sampling temperature.", min=0, max=2),
@@ -507,6 +533,7 @@ def diagram(
507
533
  diagram_type=diagram_type,
508
534
  add_documentation=add_documentation,
509
535
  splitter_type=splitter_type,
536
+ skip_context=skip_context,
510
537
  )
511
538
  diagram_generator.translate(input_dir, output_dir, overwrite, collection)
512
539
 
@@ -3,13 +3,13 @@ import json
3
3
  import math
4
4
  import time
5
5
  from pathlib import Path
6
- from typing import Any
6
+ from typing import Any, List, Optional, Tuple
7
7
 
8
8
  from langchain.output_parsers import RetryWithErrorOutputParser
9
9
  from langchain_core.exceptions import OutputParserException
10
10
  from langchain_core.language_models import BaseLanguageModel
11
11
  from langchain_core.output_parsers import BaseOutputParser
12
- from langchain_core.prompts import ChatPromptTemplate
12
+ from langchain_core.prompts import ChatPromptTemplate, SystemMessagePromptTemplate
13
13
  from langchain_core.runnables import RunnableLambda, RunnableParallel
14
14
  from openai import BadRequestError, RateLimitError
15
15
  from pydantic import ValidationError
@@ -77,6 +77,7 @@ class Converter:
77
77
  prune_node_types: tuple[str, ...] = (),
78
78
  splitter_type: str = "file",
79
79
  refiner_type: str = "basic",
80
+ skip_context: bool = False,
80
81
  ) -> None:
81
82
  """Initialize a Converter instance.
82
83
 
@@ -142,6 +143,8 @@ class Converter:
142
143
  self.set_db_path(db_path=db_path)
143
144
  self.set_db_config(db_config=db_config)
144
145
 
146
+ self.skip_context = skip_context
147
+
145
148
  # Child class must call this. Should we enforce somehow?
146
149
  # self._load_parameters()
147
150
 
@@ -602,6 +605,9 @@ class Converter:
602
605
 
603
606
  # Retries with just the input
604
607
  n3 = math.ceil(self.max_prompts / (n1 * n2))
608
+ # Make replacements in the prompt
609
+ if not self.skip_context:
610
+ self._make_prompt_additions(block)
605
611
 
606
612
  refine_output = RefinerParser(
607
613
  parser=self._parser,
@@ -648,6 +654,35 @@ class Converter:
648
654
  output=output,
649
655
  )
650
656
 
657
+ @staticmethod
658
+ def _get_prompt_additions(block) -> Optional[List[Tuple[str, str]]]:
659
+ """Get a list of strings to append to the prompt.
660
+
661
+ Arguments:
662
+ block: The `TranslatedCodeBlock` to save to a file.
663
+ """
664
+ return [(key, item) for key, item in block.context_tags.items()]
665
+
666
+ def _make_prompt_additions(self, block: CodeBlock):
667
+ # Prepare the additional context to prepend
668
+ additional_context = "".join(
669
+ [
670
+ f"{context_tag}: {context}\n"
671
+ for context_tag, context in self._get_prompt_additions(block)
672
+ ]
673
+ )
674
+
675
+ # Iterate through existing messages to find and update the system message
676
+ for i, message in enumerate(self._prompt.messages):
677
+ if isinstance(message, SystemMessagePromptTemplate):
678
+ # Prepend the additional context to the system message
679
+ updated_system_message = SystemMessagePromptTemplate.from_template(
680
+ additional_context + message.prompt.template
681
+ )
682
+ # Directly modify the message in the list
683
+ self._prompt.messages[i] = updated_system_message
684
+ break # Assuming there's only one system message to update
685
+
651
686
  def _save_to_file(self, block: TranslatedCodeBlock, out_path: Path) -> None:
652
687
  """Save a file to disk.
653
688
 
@@ -22,6 +22,11 @@ class RequirementsDocumenter(Documenter):
22
22
  self._combiner = ChunkCombiner()
23
23
  self._parser = RequirementsParser()
24
24
 
25
+ @staticmethod
26
+ def get_prompt_replacements(block) -> dict[str, str]:
27
+ prompt_replacements: dict[str, str] = {"SOURCE_CODE": block.original.text}
28
+ return prompt_replacements
29
+
25
30
  def _save_to_file(self, block: TranslatedCodeBlock, out_path: Path) -> None:
26
31
  """Save a file to disk.
27
32
 
janus/language/alc/alc.py CHANGED
@@ -1,3 +1,6 @@
1
+ import re
2
+ from typing import Optional
3
+
1
4
  from langchain.schema.language_model import BaseLanguageModel
2
5
 
3
6
  from janus.language.block import CodeBlock
@@ -61,7 +64,11 @@ class AlcSplitter(TreeSitterSplitter):
61
64
  # next csect or dsect instruction
62
65
  sects: list[list[CodeBlock]] = [[]]
63
66
  for c in block.children:
64
- if c.node_type in sect_types:
67
+ if c.node_type == "csect_instruction":
68
+ c.context_tags["alc_section"] = "CSECT"
69
+ sects.append([c])
70
+ elif c.node_type == "dsect_instruction":
71
+ c.context_tags["alc_section"] = "DSECT"
65
72
  sects.append([c])
66
73
  else:
67
74
  sects[-1].append(c)
@@ -85,3 +92,94 @@ class AlcSplitter(TreeSitterSplitter):
85
92
  queue.extend(block.children)
86
93
 
87
94
  return root
95
+
96
+
97
+ class AlcListingSplitter(AlcSplitter):
98
+ """A class for splitting ALC listing code into functional blocks to
99
+ prompt with for transcoding.
100
+ """
101
+
102
+ def __init__(
103
+ self,
104
+ model: None | BaseLanguageModel = None,
105
+ max_tokens: int = 4096,
106
+ protected_node_types: tuple[str, ...] = (),
107
+ prune_node_types: tuple[str, ...] = (),
108
+ prune_unprotected: bool = False,
109
+ ):
110
+ """Initialize a AlcSplitter instance.
111
+
112
+
113
+ Arguments:
114
+ max_tokens: The maximum number of tokens supported by the model
115
+ """
116
+ # The string to mark the end of the listing header
117
+ self.header_indicator_str: str = (
118
+ "Loc Object Code Addr1 Addr2 Stmt Source Statement"
119
+ )
120
+ # How many characters to trim from the right side to remove the address column
121
+ self.address_column_chars: int = 10
122
+ # The string to mark the end of the left margin
123
+ self.left_margin_indicator_str: str = "Stmt"
124
+ super().__init__(
125
+ model=model,
126
+ max_tokens=max_tokens,
127
+ protected_node_types=protected_node_types,
128
+ prune_node_types=prune_node_types,
129
+ prune_unprotected=prune_unprotected,
130
+ )
131
+
132
+ def _get_ast(self, code: str) -> CodeBlock:
133
+ active_usings = self.get_active_usings(code)
134
+ code = self.preproccess_assembly(code)
135
+ ast: CodeBlock = super()._get_ast(code)
136
+ ast.context_tags["active_usings"] = active_usings
137
+ return ast
138
+
139
+ def preproccess_assembly(self, code: str) -> str:
140
+ """Remove non-essential lines from an assembly snippet"""
141
+
142
+ lines = code.splitlines()
143
+ lines = self.strip_header_and_left(lines)
144
+ lines = self.strip_addresses(lines)
145
+ return "".join(str(line) for line in lines)
146
+
147
+ def get_active_usings(self, code: str) -> Optional[str]:
148
+ """Look for 'active usings' in the ALC listing header"""
149
+ lines = code.splitlines()
150
+ for line in lines:
151
+ if "Active Usings:" in line:
152
+ return line.split("Active Usings:")[1]
153
+ return None
154
+
155
+ def strip_header_and_left(
156
+ self,
157
+ lines: list[str],
158
+ ) -> list[str]:
159
+ """Remove the header and the left panel from the assembly sample"""
160
+
161
+ esd_regex = re.compile(f".*{self.header_indicator_str}.*")
162
+
163
+ header_end_index: int = [
164
+ i for i, item in enumerate(lines) if re.search(esd_regex, item)
165
+ ][0]
166
+
167
+ left_content_end_column = lines[header_end_index].find(
168
+ self.left_margin_indicator_str
169
+ )
170
+ hori_output_lines = lines[(header_end_index + 1) :]
171
+
172
+ left_output_lines = [
173
+ line[left_content_end_column + 5 :] for line in hori_output_lines
174
+ ]
175
+ return left_output_lines
176
+
177
+ def strip_addresses(self, lines: list[str]) -> list[str]:
178
+ """Strip the addresses which run down the right side of the assembly snippet"""
179
+
180
+ stripped_lines = [line[: -self.address_column_chars] for line in lines]
181
+ return stripped_lines
182
+
183
+ def strip_footer(self, lines: list[str]):
184
+ """Strip the footer from the assembly snippet"""
185
+ return NotImplementedError
janus/language/block.py CHANGED
@@ -45,6 +45,7 @@ class CodeBlock:
45
45
  children: list[ForwardRef("CodeBlock")],
46
46
  embedding_id: Optional[str] = None,
47
47
  affixes: Tuple[str, str] = ("", ""),
48
+ context_tags: dict[str, str] = {},
48
49
  ) -> None:
49
50
  self.id: Hashable = id
50
51
  self.name: Optional[str] = name
@@ -59,6 +60,7 @@ class CodeBlock:
59
60
  self.children: list[ForwardRef("CodeBlock")] = sorted(children)
60
61
  self.embedding_id: Optional[str] = embedding_id
61
62
  self.affixes: Tuple[str, str] = affixes
63
+ self.context_tags: dict[str, str] = context_tags
62
64
 
63
65
  self.complete = True
64
66
  self.omit_prefix = True
@@ -1,12 +1,24 @@
1
- from janus.language.alc.alc import AlcSplitter
1
+ from janus.language.alc.alc import AlcListingSplitter, AlcSplitter
2
2
  from janus.language.mumps.mumps import MumpsSplitter
3
3
  from janus.language.naive.registry import register_splitter
4
+ from janus.language.splitter import Splitter
4
5
  from janus.language.treesitter import TreeSitterSplitter
5
6
  from janus.utils.enums import LANGUAGES
7
+ from janus.utils.logger import create_logger
8
+
9
+ log = create_logger(__name__)
6
10
 
7
11
 
8
12
  @register_splitter("ast-flex")
9
- def get_flexible_ast(language: str, **kwargs):
13
+ def get_flexible_ast(language: str, **kwargs) -> Splitter:
14
+ """Get a flexible AST splitter for the given language.
15
+
16
+ Arguments:
17
+ language: The language to get the splitter for.
18
+
19
+ Returns:
20
+ A flexible AST splitter for the given language.
21
+ """
10
22
  if language == "ibmhlasm":
11
23
  return AlcSplitter(**kwargs)
12
24
  elif language == "mumps":
@@ -16,7 +28,17 @@ def get_flexible_ast(language: str, **kwargs):
16
28
 
17
29
 
18
30
  @register_splitter("ast-strict")
19
- def get_strict_ast(language: str, **kwargs):
31
+ def get_strict_ast(language: str, **kwargs) -> Splitter:
32
+ """Get a strict AST splitter for the given language.
33
+
34
+ The strict splitter will only return nodes that are of a functional type.
35
+
36
+ Arguments:
37
+ language: The language to get the splitter for.
38
+
39
+ Returns:
40
+ A strict AST splitter for the given language.
41
+ """
20
42
  kwargs.update(
21
43
  protected_node_types=LANGUAGES[language]["functional_node_types"],
22
44
  prune_unprotected=True,
@@ -27,3 +49,45 @@ def get_strict_ast(language: str, **kwargs):
27
49
  return MumpsSplitter(**kwargs)
28
50
  else:
29
51
  return TreeSitterSplitter(language=language, **kwargs)
52
+
53
+
54
+ @register_splitter("ast-strict-listing")
55
+ def get_strict_listing_ast(language: str, **kwargs) -> Splitter:
56
+ """Get a strict AST splitter for the given language. This splitter is intended for
57
+ use with IBM HLASM.
58
+
59
+ The strict splitter will only return nodes that are of a functional type.
60
+
61
+ Arguments:
62
+ language: The language to get the splitter for.
63
+
64
+ Returns:
65
+ A strict AST splitter for the given language.
66
+ """
67
+ kwargs.update(
68
+ protected_node_types=LANGUAGES[language]["functional_node_types"],
69
+ prune_unprotected=True,
70
+ )
71
+ if language == "ibmhlasm":
72
+ return AlcListingSplitter(**kwargs)
73
+ else:
74
+ log.warning("Listing splitter is only intended for use with IBMHLASM!")
75
+ return TreeSitterSplitter(language=language, **kwargs)
76
+
77
+
78
+ @register_splitter("ast-flex-listing")
79
+ def get_flexible_listing_ast(language: str, **kwargs) -> Splitter:
80
+ """Get a flexible AST splitter for the given language. This splitter is intended for
81
+ use with IBM HLASM.
82
+
83
+ Arguments:
84
+ language: The language to get the splitter for.
85
+
86
+ Returns:
87
+ A flexible AST splitter for the given language.
88
+ """
89
+ if language == "ibmhlasm":
90
+ return AlcListingSplitter(**kwargs)
91
+ else:
92
+ log.warning("Listing splitter is only intended for use with IBMHLASM!")
93
+ return TreeSitterSplitter(language=language, **kwargs)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: janus-llm
3
- Version: 3.3.2
3
+ Version: 3.4.0
4
4
  Summary: A transcoding library using LLMs.
5
5
  Home-page: https://github.com/janus-llm/janus-llm
6
6
  License: Apache 2.0
@@ -1,17 +1,17 @@
1
- janus/__init__.py,sha256=MyAIBSlNInMKo5GQ3bDD7TxPwy1U-UHo7831cggUHlE,361
1
+ janus/__init__.py,sha256=DDs8SRs9v96slIS8XcqcLZrUAfuwXiT9vFa6OlwYHEY,361
2
2
  janus/__main__.py,sha256=lEkpNtLVPtFo8ySDZeXJ_NXDHb0GVdZFPWB4gD4RPS8,64
3
3
  janus/_tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  janus/_tests/conftest.py,sha256=V7uW-oq3YbFiRPvrq15YoVVrA1n_83pjgiyTZ-IUGW8,963
5
5
  janus/_tests/test_cli.py,sha256=oYJsUGWfpBJWEGRG5NGxdJedU5DU_m6fwJ7xEbJVYl0,4244
6
- janus/cli.py,sha256=S6IaQyUG55xSB166xARn6TQc_cOCIQ_ZMdIi7vj6BMk,31626
6
+ janus/cli.py,sha256=Uvg6xPnJK7dJcDw1J58s3I7qwiNgPfj2qyXwpC6UGbI,32538
7
7
  janus/converter/__init__.py,sha256=U2EOMcCykiC0ZqhorNefOP_04hOF18qhYoPKrVp1Vrk,345
8
8
  janus/converter/_tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  janus/converter/_tests/test_translate.py,sha256=yzcFEGc_z8QmBBBmC9dZnfL9tT8CD1rkpc8Hz44Jp4c,5631
10
- janus/converter/converter.py,sha256=m0eGrybrPWw2B0nXrd1Fx6o0BO1JvY4PfC0rFUtpLxY,25633
10
+ janus/converter/converter.py,sha256=tBUX9rsu7filfqm6B_knafQUfNb4x4ls8so7KbEq3gs,27160
11
11
  janus/converter/diagram.py,sha256=5mo1H3Y1uIBPYdIsWz9kxluN5DNyuUMZrtcJmGF2Uw0,5335
12
12
  janus/converter/document.py,sha256=hsW512veNjFWbdl5WriuUdNmMEqZy8ktRvqn9rRmA6E,4566
13
13
  janus/converter/evaluate.py,sha256=APWQUY3gjAXqkJkPzvj0UA4wPK3Cv9QSJLM-YK9t-ng,476
14
- janus/converter/requirements.py,sha256=6YvrJRVH9BuPCOPxnXmaJQFYmoLYYvCu3zTntDLHeNg,1832
14
+ janus/converter/requirements.py,sha256=9tvQ40FZJtG8niIFn45gPQCgKKHVPPoFLinBv6RAqO4,2027
15
15
  janus/converter/translate.py,sha256=0brQTlSfBYmXtoM8QYIOiyr0LrTr0S1n68Du-BR7_WQ,4236
16
16
  janus/embedding/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  janus/embedding/_tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -29,13 +29,13 @@ janus/language/_tests/test_splitter.py,sha256=Hqexa39LLEXlK3ZUw7Zot4PUIACvye2vkq
29
29
  janus/language/alc/__init__.py,sha256=j7vOMGhT1Vri6p8dsjSaY-fkO5uFn0sJ0nrNGGvcizM,42
30
30
  janus/language/alc/_tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
31
  janus/language/alc/_tests/test_alc.py,sha256=NgVeOctm9zf-S328DdUNn9et_-lK1t5O0O2FKElb91Q,1027
32
- janus/language/alc/alc.py,sha256=CIyY3LneYToItadUGg54AeMcZL-dwx8buXB71e6EAag,3116
32
+ janus/language/alc/alc.py,sha256=l1p6zwyE7ZzY9rnjsUZGuQW41hijFrzxnXnFOpfGq8k,6590
33
33
  janus/language/binary/__init__.py,sha256=AlNAe12ZA366kcGSrQ1FJyOdbwxFqGBFkYR2K6yL818,51
34
34
  janus/language/binary/_tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
35
  janus/language/binary/_tests/test_binary.py,sha256=SDdI6tsQj9yXle7wBsksHuKULLMHv7mNgUkDx1nCvpw,1733
36
36
  janus/language/binary/binary.py,sha256=jcc-LZx8Ss-g4j0a691U2yVJV6JGV_zpE_6y_aDq4Cw,6579
37
37
  janus/language/binary/reveng/decompile_script.py,sha256=veW51oJzuO-4UD3Er062jXZ_FYtTFo9OCkl82Z2xr6A,2182
38
- janus/language/block.py,sha256=viMU3MBltiHsJXLAiIfjnlXCR-Iw2Sb-ENaK_7hUipw,9298
38
+ janus/language/block.py,sha256=2rjAYUosHFfWRgLnzf50uAgTMST4Md9Kx6JrlUfEfX4,9398
39
39
  janus/language/combine.py,sha256=Wtve06fa-_Wjv_V5RIf1Nfmg0UxcOEtFNj4vVHpSNbo,2940
40
40
  janus/language/file.py,sha256=jy-cReAoI6F97TXR5bbhPyt8XyUZCdFYnVboubDA_y4,571
41
41
  janus/language/mumps/__init__.py,sha256=-Ou_wJ-JgHezfp1dub2_qCYNiK9wO-zo2MlqxM9qiwE,48
@@ -47,7 +47,7 @@ janus/language/naive/__init__.py,sha256=_Gq4inONyVYxe8WLB59d_69kqGbtF40BGKoJPnK4
47
47
  janus/language/naive/basic_splitter.py,sha256=RM9pJK2YkHfb6_EFEV-dh_rLqkjS6v0cn3ASPf8A6Fg,459
48
48
  janus/language/naive/chunk_splitter.py,sha256=ebRSbaJhDW-Hyr5__ukbdmAl6kQ1WWFqrq_SfCgHo6k,772
49
49
  janus/language/naive/registry.py,sha256=8YQX1q0IdAm7t69-oC_00I-vfkdRnHuX-OD3KEjEIuU,294
50
- janus/language/naive/simple_ast.py,sha256=_--5mshYFt7BaZc_gAG4sSj-CTt9C1_fedP9fi2ffz4,969
50
+ janus/language/naive/simple_ast.py,sha256=T53UwAZyRfePXzpiNUhe4FyDev6YcX1dVDxYkTcDRPE,3032
51
51
  janus/language/naive/tag_splitter.py,sha256=IXWMn9tBVUGAtzvQi89GhoZ6g7fPXk5MzO0kMCr2mb0,2045
52
52
  janus/language/node.py,sha256=baoYFtapwBQqBtUN6EvHFYRkbR-EcEw1b3fQvH9zIAM,204
53
53
  janus/language/splitter.py,sha256=hITFp4a9bJ6sP74AWvC2GWa2Poo10MyHXYTj9hviXss,16970
@@ -100,8 +100,8 @@ janus/utils/_tests/test_progress.py,sha256=Rs_u5PiGjP-L-o6C1fhwfE1ig8jYu9Xo9s4p8
100
100
  janus/utils/enums.py,sha256=AoilbdiYyMvY2Mp0AM4xlbLSELfut2XMwhIM1S_msP4,27610
101
101
  janus/utils/logger.py,sha256=KZeuaMAnlSZCsj4yL0P6N-JzZwpxXygzACWfdZFeuek,2337
102
102
  janus/utils/progress.py,sha256=PIpcQec7SrhsfqB25LHj2CDDkfm9umZx90d9LZnAx6k,1469
103
- janus_llm-3.3.2.dist-info/LICENSE,sha256=_j0st0a-HB6MRbP3_BW3PUqpS16v54luyy-1zVyl8NU,10789
104
- janus_llm-3.3.2.dist-info/METADATA,sha256=NfyvyOPvC1Qj3jNh264MUYWnsA67hOD6jzL7uiIkDq4,4184
105
- janus_llm-3.3.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
106
- janus_llm-3.3.2.dist-info/entry_points.txt,sha256=OGhQwzj6pvXp79B0SaBD5apGekCu7Dwe9fZZT_TZ544,39
107
- janus_llm-3.3.2.dist-info/RECORD,,
103
+ janus_llm-3.4.0.dist-info/LICENSE,sha256=_j0st0a-HB6MRbP3_BW3PUqpS16v54luyy-1zVyl8NU,10789
104
+ janus_llm-3.4.0.dist-info/METADATA,sha256=GmgJ5Oq3MkXOpYw9Vl8YFaLx9dUNcaFZs8GQDV1t8vc,4184
105
+ janus_llm-3.4.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
106
+ janus_llm-3.4.0.dist-info/entry_points.txt,sha256=OGhQwzj6pvXp79B0SaBD5apGekCu7Dwe9fZZT_TZ544,39
107
+ janus_llm-3.4.0.dist-info/RECORD,,