palimpzest 0.7.21__py3-none-any.whl → 0.8.1__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.
Files changed (89) hide show
  1. palimpzest/__init__.py +37 -6
  2. palimpzest/agents/__init__.py +0 -0
  3. palimpzest/agents/compute_agents.py +0 -0
  4. palimpzest/agents/search_agents.py +637 -0
  5. palimpzest/constants.py +343 -209
  6. palimpzest/core/data/context.py +393 -0
  7. palimpzest/core/data/context_manager.py +163 -0
  8. palimpzest/core/data/dataset.py +639 -0
  9. palimpzest/core/data/{datareaders.py → iter_dataset.py} +202 -126
  10. palimpzest/core/elements/groupbysig.py +16 -13
  11. palimpzest/core/elements/records.py +166 -75
  12. palimpzest/core/lib/schemas.py +152 -390
  13. palimpzest/core/{data/dataclasses.py → models.py} +306 -170
  14. palimpzest/policy.py +2 -27
  15. palimpzest/prompts/__init__.py +35 -5
  16. palimpzest/prompts/agent_prompts.py +357 -0
  17. palimpzest/prompts/context_search.py +9 -0
  18. palimpzest/prompts/convert_prompts.py +62 -6
  19. palimpzest/prompts/filter_prompts.py +51 -6
  20. palimpzest/prompts/join_prompts.py +163 -0
  21. palimpzest/prompts/moa_proposer_convert_prompts.py +6 -6
  22. palimpzest/prompts/prompt_factory.py +375 -47
  23. palimpzest/prompts/split_proposer_prompts.py +1 -1
  24. palimpzest/prompts/util_phrases.py +5 -0
  25. palimpzest/prompts/validator.py +239 -0
  26. palimpzest/query/execution/all_sample_execution_strategy.py +134 -76
  27. palimpzest/query/execution/execution_strategy.py +210 -317
  28. palimpzest/query/execution/execution_strategy_type.py +5 -7
  29. palimpzest/query/execution/mab_execution_strategy.py +249 -136
  30. palimpzest/query/execution/parallel_execution_strategy.py +153 -244
  31. palimpzest/query/execution/single_threaded_execution_strategy.py +107 -64
  32. palimpzest/query/generators/generators.py +160 -331
  33. palimpzest/query/operators/__init__.py +15 -5
  34. palimpzest/query/operators/aggregate.py +50 -33
  35. palimpzest/query/operators/compute.py +201 -0
  36. palimpzest/query/operators/convert.py +33 -19
  37. palimpzest/query/operators/critique_and_refine_convert.py +7 -5
  38. palimpzest/query/operators/distinct.py +62 -0
  39. palimpzest/query/operators/filter.py +26 -16
  40. palimpzest/query/operators/join.py +403 -0
  41. palimpzest/query/operators/limit.py +3 -3
  42. palimpzest/query/operators/logical.py +205 -77
  43. palimpzest/query/operators/mixture_of_agents_convert.py +10 -8
  44. palimpzest/query/operators/physical.py +27 -21
  45. palimpzest/query/operators/project.py +3 -3
  46. palimpzest/query/operators/rag_convert.py +7 -7
  47. palimpzest/query/operators/retrieve.py +9 -9
  48. palimpzest/query/operators/scan.py +81 -42
  49. palimpzest/query/operators/search.py +524 -0
  50. palimpzest/query/operators/split_convert.py +10 -8
  51. palimpzest/query/optimizer/__init__.py +7 -9
  52. palimpzest/query/optimizer/cost_model.py +108 -441
  53. palimpzest/query/optimizer/optimizer.py +123 -181
  54. palimpzest/query/optimizer/optimizer_strategy.py +66 -61
  55. palimpzest/query/optimizer/plan.py +352 -67
  56. palimpzest/query/optimizer/primitives.py +43 -19
  57. palimpzest/query/optimizer/rules.py +484 -646
  58. palimpzest/query/optimizer/tasks.py +127 -58
  59. palimpzest/query/processor/config.py +42 -76
  60. palimpzest/query/processor/query_processor.py +73 -18
  61. palimpzest/query/processor/query_processor_factory.py +46 -38
  62. palimpzest/schemabuilder/schema_builder.py +15 -28
  63. palimpzest/utils/model_helpers.py +32 -77
  64. palimpzest/utils/progress.py +114 -102
  65. palimpzest/validator/__init__.py +0 -0
  66. palimpzest/validator/validator.py +306 -0
  67. {palimpzest-0.7.21.dist-info → palimpzest-0.8.1.dist-info}/METADATA +6 -1
  68. palimpzest-0.8.1.dist-info/RECORD +95 -0
  69. palimpzest/core/lib/fields.py +0 -141
  70. palimpzest/prompts/code_synthesis_prompts.py +0 -28
  71. palimpzest/query/execution/random_sampling_execution_strategy.py +0 -240
  72. palimpzest/query/generators/api_client_factory.py +0 -30
  73. palimpzest/query/operators/code_synthesis_convert.py +0 -488
  74. palimpzest/query/operators/map.py +0 -130
  75. palimpzest/query/processor/nosentinel_processor.py +0 -33
  76. palimpzest/query/processor/processing_strategy_type.py +0 -28
  77. palimpzest/query/processor/sentinel_processor.py +0 -88
  78. palimpzest/query/processor/streaming_processor.py +0 -149
  79. palimpzest/sets.py +0 -405
  80. palimpzest/utils/datareader_helpers.py +0 -61
  81. palimpzest/utils/demo_helpers.py +0 -75
  82. palimpzest/utils/field_helpers.py +0 -69
  83. palimpzest/utils/generation_helpers.py +0 -69
  84. palimpzest/utils/sandbox.py +0 -183
  85. palimpzest-0.7.21.dist-info/RECORD +0 -95
  86. /palimpzest/core/{elements/index.py → data/index_dataset.py} +0 -0
  87. {palimpzest-0.7.21.dist-info → palimpzest-0.8.1.dist-info}/WHEEL +0 -0
  88. {palimpzest-0.7.21.dist-info → palimpzest-0.8.1.dist-info}/licenses/LICENSE +0 -0
  89. {palimpzest-0.7.21.dist-info → palimpzest-0.8.1.dist-info}/top_level.txt +0 -0
@@ -1,17 +1,17 @@
1
1
  import logging
2
2
  from enum import Enum
3
3
 
4
+ from palimpzest.core.data.dataset import Dataset
4
5
  from palimpzest.core.elements.records import DataRecordCollection
5
6
  from palimpzest.query.execution.execution_strategy import ExecutionStrategy, SentinelExecutionStrategy
6
7
  from palimpzest.query.execution.execution_strategy_type import ExecutionStrategyType, SentinelExecutionStrategyType
7
- from palimpzest.query.optimizer.cost_model import CostModel
8
+ from palimpzest.query.optimizer.cost_model import SampleBasedCostModel
8
9
  from palimpzest.query.optimizer.optimizer import Optimizer
9
10
  from palimpzest.query.optimizer.optimizer_strategy_type import OptimizationStrategyType
10
11
  from palimpzest.query.processor.config import QueryProcessorConfig
11
- from palimpzest.query.processor.processing_strategy_type import ProcessingStrategyType
12
12
  from palimpzest.query.processor.query_processor import QueryProcessor
13
- from palimpzest.sets import Dataset, Set
14
13
  from palimpzest.utils.model_helpers import get_models
14
+ from palimpzest.validator.validator import Validator
15
15
 
16
16
  logger = logging.getLogger(__name__)
17
17
 
@@ -33,12 +33,11 @@ class QueryProcessorFactory:
33
33
  an exception if the conversion fails.
34
34
  """
35
35
  strategy_types = {
36
- "processing_strategy": ProcessingStrategyType,
37
36
  "execution_strategy": ExecutionStrategyType,
38
37
  "sentinel_execution_strategy": SentinelExecutionStrategyType,
39
38
  "optimizer_strategy": OptimizationStrategyType,
40
39
  }
41
- for strategy in ["processing_strategy", "execution_strategy", "sentinel_execution_strategy", "optimizer_strategy"]:
40
+ for strategy in ["execution_strategy", "sentinel_execution_strategy", "optimizer_strategy"]:
42
41
  strategy_str = getattr(config, strategy)
43
42
  strategy_type = strategy_types[strategy]
44
43
  strategy_enum = None
@@ -54,57 +53,62 @@ class QueryProcessorFactory:
54
53
  return config
55
54
 
56
55
  @classmethod
57
- def _config_validation_and_normalization(cls, config: QueryProcessorConfig):
56
+ def _config_validation_and_normalization(cls, config: QueryProcessorConfig, train_dataset: dict[str, Dataset] | None, validator : Validator | None):
58
57
  if config.policy is None:
59
58
  raise ValueError("Policy is required for optimizer")
60
-
61
- if config.cache:
62
- raise ValueError("cache=True is not supported yet")
63
59
 
64
60
  # only one of progress or verbose can be set; we will default to progress=True
65
61
  if config.progress and config.verbose:
66
62
  print("WARNING: Both `progress` and `verbose` are set to True, but only one can be True at a time; defaulting to `progress=True`")
67
63
  config.verbose = False
68
64
 
69
- # handle "auto" defaults for processing and sentinel execution strategies
70
- if config.processing_strategy == "auto":
71
- config.processing_strategy = "no_sentinel" if config.val_datasource is None else "sentinel"
65
+ # boolean flag for whether we're performing optimization or not
66
+ optimization = train_dataset is not None or validator is not None
67
+ val_based_opt = train_dataset is None and validator is not None
72
68
 
69
+ # handle "auto" default for sentinel execution strategies
73
70
  if config.sentinel_execution_strategy == "auto":
74
- config.sentinel_execution_strategy = None if config.val_datasource is None else "mab"
71
+ config.sentinel_execution_strategy = ("validator" if val_based_opt else "mab") if optimization else None
75
72
 
76
73
  # convert the config values for processing, execution, and optimization strategies to enums
77
74
  config = cls._normalize_strategies(config)
78
75
 
79
- # check that processor uses a supported execution strategy
80
- if config.execution_strategy not in config.processing_strategy.valid_execution_strategies():
81
- raise ValueError(f"Unsupported `execution_strategy` {config.execution_strategy} for `processing_strategy` {config.processing_strategy}.")
82
-
83
- # check that validation data is provided for sentinel execution
84
- if config.val_datasource is None and config.processing_strategy.is_sentinel_strategy():
85
- raise ValueError("`val_datasource` is required for SENTINEL processing strategies")
86
-
87
- # check that sentinel execution is provided for sentinel processor
88
- if config.sentinel_execution_strategy is None and config.processing_strategy.is_sentinel_strategy():
89
- raise ValueError("`sentinel_execution_strategy` is required for SENTINEL processing strategies")
90
-
91
76
  # get available models
92
77
  available_models = getattr(config, 'available_models', [])
93
78
  if available_models is None or len(available_models) == 0:
94
- available_models = get_models(include_vision=True)
79
+ available_models = get_models(use_vertex=config.use_vertex, gemini_credentials_path=config.gemini_credentials_path, api_base=config.api_base)
80
+
81
+ # remove any models specified in the config
82
+ remove_models = getattr(config, 'remove_models', [])
83
+ if remove_models is not None and len(remove_models) > 0:
84
+ available_models = [model for model in available_models if model not in remove_models]
85
+ logger.info(f"Removed models from available models based on config: {remove_models}")
86
+
87
+ # set the final set of available models in the config
95
88
  config.available_models = available_models
96
89
 
97
90
  return config
98
91
 
99
92
  @classmethod
100
93
  def _create_optimizer(cls, config: QueryProcessorConfig) -> Optimizer:
101
- return Optimizer(cost_model=CostModel(), **config.to_dict())
94
+ return Optimizer(cost_model=SampleBasedCostModel(), **config.to_dict())
102
95
 
103
96
  @classmethod
104
- def _create_execution_strategy(cls, config: QueryProcessorConfig) -> ExecutionStrategy:
97
+ def _create_execution_strategy(cls, dataset: Dataset, config: QueryProcessorConfig) -> ExecutionStrategy:
105
98
  """
106
99
  Creates an execution strategy based on the configuration.
107
100
  """
101
+ # for parallel execution, set the batch size if there's a limit in the query
102
+ limit = dataset.get_limit()
103
+ if limit is not None and config.execution_strategy == ExecutionStrategyType.PARALLEL:
104
+ if config.batch_size is None:
105
+ config.batch_size = limit
106
+ logger.info(f"Setting batch size to query limit: {limit}")
107
+ elif config.batch_size > limit:
108
+ config.batch_size = limit
109
+ logger.info(f"Setting batch size to query limit: {limit} since it was larger than the limit")
110
+
111
+ # create the execution strategy
108
112
  execution_strategy_cls = config.execution_strategy.value
109
113
  return execution_strategy_cls(**config.to_dict())
110
114
 
@@ -122,9 +126,10 @@ class QueryProcessorFactory:
122
126
  @classmethod
123
127
  def create_processor(
124
128
  cls,
125
- dataset: Set,
129
+ dataset: Dataset,
126
130
  config: QueryProcessorConfig | None = None,
127
- **kwargs
131
+ train_dataset: dict[str, Dataset] | None = None,
132
+ validator: Validator | None = None,
128
133
  ) -> QueryProcessor:
129
134
  """
130
135
  Creates a QueryProcessor with specified processing and execution strategies.
@@ -138,23 +143,26 @@ class QueryProcessorFactory:
138
143
  config = QueryProcessorConfig()
139
144
 
140
145
  # apply any additional keyword arguments to the config and validate its contents
141
- config.update(**kwargs)
142
- config = cls._config_validation_and_normalization(config)
146
+ config = cls._config_validation_and_normalization(config, train_dataset, validator)
143
147
 
144
148
  # create the optimizer, execution strateg(ies), and processor
145
149
  optimizer = cls._create_optimizer(config)
146
- config.execution_strategy = cls._create_execution_strategy(config)
150
+ config.execution_strategy = cls._create_execution_strategy(dataset, config)
147
151
  config.sentinel_execution_strategy = cls._create_sentinel_execution_strategy(config)
148
- processor_cls = config.processing_strategy.value
149
- processor = processor_cls(dataset, optimizer, **config.to_dict())
152
+ processor = QueryProcessor(dataset, optimizer, train_dataset=train_dataset, validator=validator, **config.to_dict())
150
153
 
151
154
  return processor
152
155
 
153
156
  @classmethod
154
- def create_and_run_processor(cls, dataset: Dataset, config: QueryProcessorConfig | None = None, **kwargs) -> DataRecordCollection:
155
- # TODO(Jun): Consider to use cache here.
157
+ def create_and_run_processor(
158
+ cls,
159
+ dataset: Dataset,
160
+ config: QueryProcessorConfig | None = None,
161
+ train_dataset: dict[str, Dataset] | None = None,
162
+ validator: Validator | None = None,
163
+ ) -> DataRecordCollection:
156
164
  logger.info(f"Creating processor for dataset: {dataset}")
157
- processor = cls.create_processor(dataset=dataset, config=config, **kwargs)
165
+ processor = cls.create_processor(dataset, config, train_dataset, validator)
158
166
  logger.info(f"Created processor: {processor}")
159
167
 
160
168
  return processor.execute()
@@ -7,15 +7,15 @@ This method is a simple wrapper for different methods, e.g., from_csv, from_yml,
7
7
 
8
8
  import json
9
9
  import os
10
+ from typing import Any
10
11
 
11
12
  import pandas as pd
12
13
  import yaml
14
+ from pydantic import BaseModel
13
15
  from pyld import jsonld
14
16
 
15
- import palimpzest.core.lib.fields as pz_fields
16
17
  import palimpzest.core.lib.schemas as pz_schemas
17
- from palimpzest.core.lib.fields import Field
18
- from palimpzest.core.lib.schemas import Schema
18
+ from palimpzest.core.lib.schemas import create_schema_from_fields
19
19
 
20
20
 
21
21
  class SchemaBuilder:
@@ -24,19 +24,17 @@ class SchemaBuilder:
24
24
  def from_file(cls,
25
25
  schema_file: str,
26
26
  schema_name: str = "",
27
- schema_description: str = "",
28
27
  include_attributes: list = None,
29
28
  exclude_attributes: list = None,
30
- schema_type: Schema = None,
29
+ schema_type: BaseModel = None,
31
30
  ):
32
31
  """
33
32
  Inputs:
34
33
  schema_file: str - the path to the file
35
- description (optional): str - the description of the schema
36
34
  name (optional): str - the name of the schema
37
35
  include_attributes (optional): list - a list of attribute names to include in the schema. If None, all attributes are included.
38
36
  exclude_attributes (optional): list - a list of attribute names to exclude from the schema. If None, no attributes are excluded.
39
- schema_type (optional): Schema - the parent type of the schema to generate, e.g. ScientificPapers have a schema_type of PDFFile. If None, a generic Schema type is used.
37
+ schema_type (optional): BaseModel - the parent type of the schema to generate, e.g. ScientificPapers have a schema_type of PDFFile. If None, a generic Schema type is used.
40
38
  Outputs:
41
39
  A class object - the dynamically generated class
42
40
  """
@@ -64,12 +62,6 @@ class SchemaBuilder:
64
62
  else:
65
63
  schema_name = "".join([word.capitalize() for word in basename.split("_")])
66
64
 
67
- if not schema_description:
68
- if schema_data['description']:
69
- schema_description = schema_data['description']
70
- else:
71
- schema_description = f"A schema generated from the {file_extension} file {basename}."
72
-
73
65
  if include_attributes is None:
74
66
  include_attributes = []
75
67
  if exclude_attributes is None:
@@ -78,14 +70,16 @@ class SchemaBuilder:
78
70
  if schema_type is None:
79
71
  if schema_data.get('type', None):
80
72
  # Find if the schema type is a valid class in pz
81
- parsed_type = getattr(pz_schemas
82
- , schema_data['type'], Schema)
83
- schema_type = parsed_type if issubclass(parsed_type, Schema) else Schema
73
+ parsed_type = getattr(pz_schemas, schema_data['type'], BaseModel)
74
+ schema_type = parsed_type if issubclass(parsed_type, BaseModel) else BaseModel
84
75
  else:
85
- schema_type = Schema
76
+ schema_type = BaseModel
86
77
 
87
78
  # Generate the schema class dynamically
88
- attributes = {"__doc__": schema_description}
79
+ fields = [
80
+ {"name": field_name, "description": field.description, "type": field.annotation}
81
+ for field_name, field in schema_type.model_fields.items()
82
+ ]
89
83
  include_attributes_lower = set([a.lower() for a in include_attributes])
90
84
  exclude_attributes_lower = set([a.lower() for a in exclude_attributes])
91
85
  for field in schema_data['fields']:
@@ -96,15 +90,9 @@ class SchemaBuilder:
96
90
  continue
97
91
  name = field['name']
98
92
  description = field.get('description', '')
99
- field_type = field.get('type', 'Field')
100
- field_type = getattr(pz_fields, field_type, Field)
101
- if not issubclass(field_type, Field):
102
- field_type = Field
103
-
104
- attributes[name] = field_type(desc=description)
93
+ fields.append({"name": name, "description": description, "type": Any})
105
94
 
106
- # Create the class dynamically
107
- return type(schema_name, (schema_type,), attributes)
95
+ return create_schema_from_fields(fields)
108
96
 
109
97
  @classmethod
110
98
  def from_csv(
@@ -189,9 +177,8 @@ class SchemaBuilder:
189
177
  cls,
190
178
  schema_file: str,
191
179
  schema_name: str = "",
192
- schema_description: str = "",
193
180
  include_attributes: list = None,
194
- schema_type: Schema = None,
181
+ schema_type: BaseModel = None,
195
182
  )-> dict:
196
183
  """
197
184
  The attributes are extracted from the JSON objects.
@@ -3,29 +3,8 @@ import os
3
3
  from palimpzest.constants import Model
4
4
 
5
5
 
6
- def get_vision_models() -> list[Model]:
7
- """
8
- Return the set of vision models which the system has access to based on the set of environment variables.
9
- """
10
- models = []
11
- if os.getenv("OPENAI_API_KEY") is not None:
12
- openai_vision_models = [
13
- model for model in Model
14
- if model.is_openai_model() and model.is_vision_model()
15
- ]
16
- models.extend(openai_vision_models)
17
-
18
- if os.getenv("TOGETHER_API_KEY") is not None:
19
- together_vision_models = [
20
- model for model in Model
21
- if model.is_together_model() and model.is_vision_model()
22
- ]
23
- models.extend(together_vision_models)
24
-
25
- return models
26
-
27
-
28
- def get_models(include_vision: bool = False, include_embedding: bool = False) -> list[Model]:
6
+ # TODO: better handle vertex vs. google for gemini models
7
+ def get_models(include_embedding: bool = False, use_vertex: bool = True, gemini_credentials_path: str | None = None, api_base: str | None = None) -> list[Model]:
29
8
  """
30
9
  Return the set of models which the system has access to based on the set environment variables.
31
10
  """
@@ -40,67 +19,43 @@ def get_models(include_vision: bool = False, include_embedding: bool = False) ->
40
19
 
41
20
  if os.getenv("TOGETHER_API_KEY") is not None:
42
21
  together_models = [model for model in Model if model.is_together_model()]
43
- if not include_vision:
44
- together_models = [
45
- model for model in together_models if not model.is_vision_model()
46
- ]
47
22
  if not include_embedding:
48
23
  together_models = [
49
24
  model for model in together_models if not model.is_embedding_model()
50
25
  ]
51
26
  models.extend(together_models)
52
27
 
53
- if include_vision:
54
- vision_models = get_vision_models()
55
- models.extend(vision_models)
56
-
57
- return models
58
-
59
- # The order is the priority of the model
60
- TEXT_MODEL_PRIORITY = [
61
- # Model.o1,
62
- Model.GPT_4o,
63
- Model.GPT_4o_MINI,
64
- Model.LLAMA3_3_70B,
65
- Model.MIXTRAL,
66
- Model.DEEPSEEK_V3,
67
- Model.LLAMA3_2_3B,
68
- Model.LLAMA3_1_8B,
69
- Model.DEEPSEEK_R1_DISTILL_QWEN_1_5B,
70
- ]
71
-
72
- VISION_MODEL_PRIORITY = [
73
- Model.GPT_4o,
74
- Model.GPT_4o_MINI,
75
- Model.LLAMA3_2_90B_V,
76
- ]
77
- def get_champion_model(available_models, vision=False):
78
- # Select appropriate priority list based on task
79
- model_priority = VISION_MODEL_PRIORITY if vision else TEXT_MODEL_PRIORITY
80
-
81
- # Return first available model from priority list
82
- for model in model_priority:
83
- if model in available_models:
84
- return model
28
+ if os.getenv("ANTHROPIC_API_KEY") is not None:
29
+ anthropic_models = [model for model in Model if model.is_anthropic_model()]
30
+ if not include_embedding:
31
+ anthropic_models = [
32
+ model for model in anthropic_models if not model.is_embedding_model()
33
+ ]
34
+ models.extend(anthropic_models)
85
35
 
86
- # If no suitable model found, raise informative error
87
- task_type = "vision" if vision else "text"
88
- raise Exception(
89
- f"No {task_type} models available to create physical plans!\n"
90
- "You must set at least one of the following environment variables:\n"
91
- "[OPENAI_API_KEY, TOGETHER_API_KEY, GOOGLE_API_KEY]\n"
92
- f"Available models: {available_models}"
36
+ gemini_credentials_path = (
37
+ os.path.join(os.path.expanduser("~"), ".config", "gcloud", "application_default_credentials.json")
38
+ if gemini_credentials_path is None
39
+ else gemini_credentials_path
93
40
  )
41
+ if os.getenv("GEMINI_API_KEY") is not None or os.path.exists(gemini_credentials_path):
42
+ vertex_models = [model for model in Model if model.is_vertex_model()]
43
+ google_models = [model for model in Model if model.is_google_model()]
44
+ if not include_embedding:
45
+ vertex_models = [
46
+ model for model in vertex_models if not model.is_embedding_model()
47
+ ]
48
+ if use_vertex:
49
+ models.extend(vertex_models)
50
+ else:
51
+ models.extend(google_models)
94
52
 
53
+ if api_base is not None:
54
+ vllm_models = [model for model in Model if model.is_vllm_model()]
55
+ if not include_embedding:
56
+ vllm_models = [
57
+ model for model in vllm_models if not model.is_embedding_model()
58
+ ]
59
+ models.extend(vllm_models)
95
60
 
96
- def get_fallback_model(available_models, vision=False):
97
- return get_champion_model(available_models, vision)
98
-
99
-
100
- def get_code_champion_model(available_models):
101
- # NOTE: for now, assume same champion as get_champion_model()
102
- return get_champion_model(available_models, vision=False)
103
-
104
-
105
- def get_champion_model_name(available_models, vision=False):
106
- return get_champion_model(available_models, vision).value
61
+ return models