hyb
2025-11-07 cadac0a99d87c53805a07f3b4ca7fd11e524fe4a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# Copyright (c) 2025 Oracle and/or its affiliates.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2.0, as
# published by the Free Software Foundation.
#
# This program is designed to work with certain software (including
# but not limited to OpenSSL) that is licensed under separate terms,
# as designated in a particular file or component or in included license
# documentation. The authors of MySQL hereby grant you an
# additional permission to link the program and your derivative works
# with the separately licensed software that they have either included with
# the program or referenced in the documentation.
#
# Without limiting anything contained in the foregoing, this file,
# which is part of MySQL Connector/Python, is also subject to the
# Universal FOSS Exception, version 1.0, a copy of which can be found at
# http://oss.oracle.com/licenses/universal-foss-exception.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License, version 2.0, for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
 
"""GenAI LLM integration utilities for MySQL Connector/Python.
 
Provides MyLLM wrapper that issues ML_GENERATE calls via SQL.
"""
 
import json
 
from typing import Any, List, Optional
 
try:
    from langchain_core.language_models.llms import LLM
except ImportError:
    from langchain.llms.base import LLM
from pydantic import PrivateAttr
 
from mysql.ai.utils import atomic_transaction, execute_sql, format_value_sql
from mysql.connector.abstracts import MySQLConnectionAbstract
 
 
class MyLLM(LLM):
    """
    Custom Large Language Model (LLM) interface for MySQL HeatWave.
 
    This class wraps the generation functionality provided by HeatWave LLMs,
    exposing an interface compatible with common LLM APIs for text generation.
    It provides full support for generative queries and limited support for
    agentic queries.
 
    Attributes:
        _db_connection (MySQLConnectionAbstract):
            Underlying MySQL connector database connection.
    """
 
    _db_connection: MySQLConnectionAbstract = PrivateAttr()
 
    class Config:
        """
        Pydantic config for the model.
 
        By default, LangChain (through Pydantic BaseModel) does not allow
        setting or storing undeclared attributes such as _db_connection.
        Setting extra = "allow" makes it possible to store extra attributes
        on the class instance, which is required for MyLLM.
        """
 
        extra = "allow"
 
    def __init__(self, db_connection: MySQLConnectionAbstract):
        """
        Initialize the MyLLM instance with an active MySQL database connection.
 
        Args:
            db_connection: A MySQL connection object used to run LLM queries.
 
        Notes:
            The db_connection is stored as a private attribute via object.__setattr__,
            which is compatible with Pydantic models.
        """
        super().__init__()
 
        self._db_connection = db_connection
 
    def _call(
        self,
        prompt: str,
        stop: Optional[List[str]] = None,
        **kwargs: Any,
    ) -> str:
        """
        Generate a text completion from the LLM for a given input prompt.
 
        References:
            https://dev.mysql.com/doc/heatwave/en/mys-hwgenai-ml-generate.html
                A full list of supported options (specified by kwargs) can be found under "options"
 
        Args:
            prompt: The input prompt string for the language model.
            stop: Optional list of stop strings to support agentic and chain-of-thought
                  reasoning workflows.
            **kwargs: Additional keyword arguments providing generation options to
                      the LLM (these are serialized to JSON and passed to the HeatWave syscall).
 
        Returns:
            The generated model output as a string.
            (The actual completion does NOT include the input prompt.)
 
        Raises:
            DatabaseError:
                If provided options are invalid or unsupported.
                If a database connection issue occurs.
                If an operational error occurs during execution.
 
        Implementation Notes:
            - Serializes kwargs into a SQL-compatible JSON string.
            - Calls the LLM stored procedure using a database cursor context.
            - Uses `sys.ML_GENERATE` on the server to produce the model output.
            - Expects the server response to be a JSON object with a 'text' key.
        """
        options = kwargs.copy()
        if stop is not None:
            options["stop_sequences"] = stop
 
        options_placeholder, options_params = format_value_sql(options)
        with atomic_transaction(self._db_connection) as cursor:
            # The prompt is passed as a parameterized argument (avoids SQL injection).
            generate_query = f"""SELECT sys.ML_GENERATE("%s", {options_placeholder});"""
            execute_sql(cursor, generate_query, params=(prompt, *options_params))
            # Expect a JSON-encoded result from MySQL; parse to extract the output.
            llm_response = json.loads(cursor.fetchone()[0])["text"]
 
        return llm_response
 
    @property
    def _identifying_params(self) -> dict:
        """
        Return a dictionary of params that uniquely identify this LLM instance.
 
        Returns:
            dict: Dictionary of identifier parameters (should include
                model_name for tracing/caching).
        """
        return {
            "model_name": "mysql_heatwave_llm",
        }
 
    @property
    def _llm_type(self) -> str:
        """
        Get the type name of this LLM implementation.
 
        Returns:
            A string identifying the LLM provider (used for logging or metrics).
        """
        return "mysql_heatwave_llm"