|
| 1 | +"""Detail-pane renderers for the add-model browser. |
| 2 | +
|
| 3 | +Plain ANSI strings over the active theme palette; consumed as termflow |
| 4 | +Menu previews by :mod:`code_puppy.command_line.add_model_menu` (split |
| 5 | +out of that module purely for the 600-line cap). |
| 6 | +""" |
| 7 | + |
| 8 | +from code_puppy.models_dev_parser import ModelInfo, ProviderInfo |
| 9 | +from code_puppy.provider_credentials import ( |
| 10 | + credential_display, |
| 11 | + credential_hint, |
| 12 | + is_credential_set, |
| 13 | +) |
| 14 | + |
| 15 | + |
| 16 | +def _unsupported() -> dict: |
| 17 | + # Lazy import: add_model_menu imports this module at load time. |
| 18 | + from code_puppy.command_line.add_model_menu import UNSUPPORTED_PROVIDERS |
| 19 | + |
| 20 | + return UNSUPPORTED_PROVIDERS |
| 21 | + |
| 22 | + |
| 23 | +def _style(): |
| 24 | + from termflow.render.style import RenderStyle |
| 25 | + |
| 26 | + from code_puppy.command_line.tui_style import menu_style |
| 27 | + |
| 28 | + return menu_style() or RenderStyle.default() |
| 29 | + |
| 30 | + |
| 31 | +def _ansi(color: str, text: str) -> str: |
| 32 | + from termflow.ansi.codes import RESET |
| 33 | + from termflow.ansi.color import fg_color |
| 34 | + |
| 35 | + return f"{fg_color(color)}{text}{RESET}" |
| 36 | + |
| 37 | + |
| 38 | +def provider_details(provider: ProviderInfo) -> str: |
| 39 | + s = _style() |
| 40 | + lines = [ |
| 41 | + _ansi(s.bright, "MODEL DETAILS"), |
| 42 | + "", |
| 43 | + _ansi(s.head, f"{provider.name}"), |
| 44 | + f"ID: {provider.id}", |
| 45 | + f"Models: {provider.model_count}", |
| 46 | + f"API: {provider.api}", |
| 47 | + ] |
| 48 | + if provider.id in _unsupported(): |
| 49 | + lines += [ |
| 50 | + "", |
| 51 | + _ansi(s.error, "UNSUPPORTED PROVIDER"), |
| 52 | + _ansi(s.error, _unsupported()[provider.id]), |
| 53 | + _ansi(s.grey, "Models from this provider cannot be added."), |
| 54 | + ] |
| 55 | + if provider.env: |
| 56 | + lines += ["", _ansi(s.head, "Credentials:")] |
| 57 | + for env_var in provider.env: |
| 58 | + color = s.head if is_credential_set(env_var) else s.error |
| 59 | + lines.append(_ansi(color, f" {env_var}: {credential_display(env_var)}")) |
| 60 | + hint = credential_hint(env_var) |
| 61 | + if hint: |
| 62 | + lines.append(_ansi(s.grey, f" {hint}")) |
| 63 | + if provider.doc: |
| 64 | + lines += ["", _ansi(s.head, "Documentation:"), f" {provider.doc}"] |
| 65 | + return "\n".join(lines) |
| 66 | + |
| 67 | + |
| 68 | +def custom_model_details(provider: ProviderInfo) -> str: |
| 69 | + s = _style() |
| 70 | + lines = [ |
| 71 | + _ansi(s.bright, "MODEL DETAILS"), |
| 72 | + "", |
| 73 | + _ansi(s.head, "Custom Model"), |
| 74 | + "Add a model not listed in models.dev", |
| 75 | + "", |
| 76 | + _ansi(s.grey, "1. Press Enter to select"), |
| 77 | + _ansi(s.grey, "2. Enter the model ID/name"), |
| 78 | + _ansi(s.grey, f"3. Uses {provider.name}'s API endpoint"), |
| 79 | + ] |
| 80 | + if provider.env: |
| 81 | + lines += ["", _ansi(s.head, "Required credentials:")] |
| 82 | + lines += [_ansi(s.grey, f" {env_var}") for env_var in provider.env] |
| 83 | + return "\n".join(lines) |
| 84 | + |
| 85 | + |
| 86 | +def model_details(model: ModelInfo, provider: ProviderInfo) -> str: |
| 87 | + s = _style() |
| 88 | + lines = [ |
| 89 | + _ansi(s.bright, "MODEL DETAILS"), |
| 90 | + "", |
| 91 | + _ansi(s.head, f"{provider.name} - {model.name}"), |
| 92 | + ] |
| 93 | + if not model.tool_call: |
| 94 | + lines += [ |
| 95 | + "", |
| 96 | + _ansi(s.error, "NO TOOL CALLING SUPPORT"), |
| 97 | + _ansi(s.error, "This model cannot use tools (file ops, shell"), |
| 98 | + _ansi(s.error, "commands, etc). Very limited for coding tasks!"), |
| 99 | + ] |
| 100 | + lines += ["", _ansi(s.head, "Capabilities:")] |
| 101 | + for cap_name, has_cap in ( |
| 102 | + ("Vision", model.has_vision), |
| 103 | + ("Tool Calling", model.tool_call), |
| 104 | + ("Reasoning", model.reasoning), |
| 105 | + ("Temperature", model.temperature), |
| 106 | + ("Structured Output", model.structured_output), |
| 107 | + ("Attachments", model.attachment), |
| 108 | + ): |
| 109 | + mark = "+" if has_cap else "-" |
| 110 | + lines.append(_ansi(s.head if has_cap else s.grey, f" {mark} {cap_name}")) |
| 111 | + lines += ["", _ansi(s.head, "Pricing:")] |
| 112 | + if model.cost_input is not None or model.cost_output is not None: |
| 113 | + if model.cost_input is not None: |
| 114 | + lines.append(_ansi(s.grey, f" Input: ${model.cost_input:.6f}/token")) |
| 115 | + if model.cost_output is not None: |
| 116 | + lines.append(_ansi(s.grey, f" Output: ${model.cost_output:.6f}/token")) |
| 117 | + if model.cost_cache_read is not None: |
| 118 | + lines.append( |
| 119 | + _ansi(s.grey, f" Cache Read: ${model.cost_cache_read:.6f}/token") |
| 120 | + ) |
| 121 | + else: |
| 122 | + lines.append(_ansi(s.grey, " Pricing not available")) |
| 123 | + lines += ["", _ansi(s.head, "Limits:")] |
| 124 | + if model.context_length > 0: |
| 125 | + lines.append(_ansi(s.grey, f" Context: {model.context_length:,} tokens")) |
| 126 | + if model.max_output > 0: |
| 127 | + lines.append(_ansi(s.grey, f" Max Output: {model.max_output:,} tokens")) |
| 128 | + if model.input_modalities or model.output_modalities: |
| 129 | + lines += ["", _ansi(s.head, "Modalities:")] |
| 130 | + if model.input_modalities: |
| 131 | + lines.append(_ansi(s.grey, f" Input: {', '.join(model.input_modalities)}")) |
| 132 | + if model.output_modalities: |
| 133 | + lines.append( |
| 134 | + _ansi(s.grey, f" Output: {', '.join(model.output_modalities)}") |
| 135 | + ) |
| 136 | + lines += ["", _ansi(s.head, "Metadata:")] |
| 137 | + lines.append(_ansi(s.grey, f" Model ID: {model.model_id}")) |
| 138 | + lines.append(_ansi(s.grey, f" Full ID: {model.full_id}")) |
| 139 | + if model.knowledge: |
| 140 | + lines.append(_ansi(s.grey, f" Knowledge: {model.knowledge}")) |
| 141 | + if model.release_date: |
| 142 | + lines.append(_ansi(s.grey, f" Released: {model.release_date}")) |
| 143 | + lines.append(_ansi(s.grey, f" Open Weights: {model.open_weights}")) |
| 144 | + return "\n".join(lines) |
0 commit comments