You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					348 lines
				
				10 KiB
			
		
		
			
		
	
	
					348 lines
				
				10 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								import json
							 | 
						||
| 
								 | 
							
								from copy import deepcopy
							 | 
						||
| 
								 | 
							
								from typing import Optional, Tuple, Union
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import openai
							 | 
						||
| 
								 | 
							
								from openai import api_requestor, util
							 | 
						||
| 
								 | 
							
								from openai.openai_response import OpenAIResponse
							 | 
						||
| 
								 | 
							
								from openai.util import ApiType
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class OpenAIObject(dict):
							 | 
						||
| 
								 | 
							
								    api_base_override = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(
							 | 
						||
| 
								 | 
							
								        self,
							 | 
						||
| 
								 | 
							
								        id=None,
							 | 
						||
| 
								 | 
							
								        api_key=None,
							 | 
						||
| 
								 | 
							
								        api_version=None,
							 | 
						||
| 
								 | 
							
								        api_type=None,
							 | 
						||
| 
								 | 
							
								        organization=None,
							 | 
						||
| 
								 | 
							
								        response_ms: Optional[int] = None,
							 | 
						||
| 
								 | 
							
								        api_base=None,
							 | 
						||
| 
								 | 
							
								        engine=None,
							 | 
						||
| 
								 | 
							
								        **params,
							 | 
						||
| 
								 | 
							
								    ):
							 | 
						||
| 
								 | 
							
								        super(OpenAIObject, self).__init__()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if response_ms is not None and not isinstance(response_ms, int):
							 | 
						||
| 
								 | 
							
								            raise TypeError(f"response_ms is a {type(response_ms).__name__}.")
							 | 
						||
| 
								 | 
							
								        self._response_ms = response_ms
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._retrieve_params = params
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        object.__setattr__(self, "api_key", api_key)
							 | 
						||
| 
								 | 
							
								        object.__setattr__(self, "api_version", api_version)
							 | 
						||
| 
								 | 
							
								        object.__setattr__(self, "api_type", api_type)
							 | 
						||
| 
								 | 
							
								        object.__setattr__(self, "organization", organization)
							 | 
						||
| 
								 | 
							
								        object.__setattr__(self, "api_base_override", api_base)
							 | 
						||
| 
								 | 
							
								        object.__setattr__(self, "engine", engine)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if id:
							 | 
						||
| 
								 | 
							
								            self["id"] = id
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def response_ms(self) -> Optional[int]:
							 | 
						||
| 
								 | 
							
								        return self._response_ms
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __setattr__(self, k, v):
							 | 
						||
| 
								 | 
							
								        if k[0] == "_" or k in self.__dict__:
							 | 
						||
| 
								 | 
							
								            return super(OpenAIObject, self).__setattr__(k, v)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self[k] = v
							 | 
						||
| 
								 | 
							
								        return None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __getattr__(self, k):
							 | 
						||
| 
								 | 
							
								        if k[0] == "_":
							 | 
						||
| 
								 | 
							
								            raise AttributeError(k)
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            return self[k]
							 | 
						||
| 
								 | 
							
								        except KeyError as err:
							 | 
						||
| 
								 | 
							
								            raise AttributeError(*err.args)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __delattr__(self, k):
							 | 
						||
| 
								 | 
							
								        if k[0] == "_" or k in self.__dict__:
							 | 
						||
| 
								 | 
							
								            return super(OpenAIObject, self).__delattr__(k)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            del self[k]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __setitem__(self, k, v):
							 | 
						||
| 
								 | 
							
								        if v == "":
							 | 
						||
| 
								 | 
							
								            raise ValueError(
							 | 
						||
| 
								 | 
							
								                "You cannot set %s to an empty string. "
							 | 
						||
| 
								 | 
							
								                "We interpret empty strings as None in requests."
							 | 
						||
| 
								 | 
							
								                "You may set %s.%s = None to delete the property" % (k, str(self), k)
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        super(OpenAIObject, self).__setitem__(k, v)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __delitem__(self, k):
							 | 
						||
| 
								 | 
							
								        raise NotImplementedError("del is not supported")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # Custom unpickling method that uses `update` to update the dictionary
							 | 
						||
| 
								 | 
							
								    # without calling __setitem__, which would fail if any value is an empty
							 | 
						||
| 
								 | 
							
								    # string
							 | 
						||
| 
								 | 
							
								    def __setstate__(self, state):
							 | 
						||
| 
								 | 
							
								        self.update(state)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # Custom pickling method to ensure the instance is pickled as a custom
							 | 
						||
| 
								 | 
							
								    # class and not as a dict, otherwise __setstate__ would not be called when
							 | 
						||
| 
								 | 
							
								    # unpickling.
							 | 
						||
| 
								 | 
							
								    def __reduce__(self):
							 | 
						||
| 
								 | 
							
								        reduce_value = (
							 | 
						||
| 
								 | 
							
								            type(self),  # callable
							 | 
						||
| 
								 | 
							
								            (  # args
							 | 
						||
| 
								 | 
							
								                self.get("id", None),
							 | 
						||
| 
								 | 
							
								                self.api_key,
							 | 
						||
| 
								 | 
							
								                self.api_version,
							 | 
						||
| 
								 | 
							
								                self.api_type,
							 | 
						||
| 
								 | 
							
								                self.organization,
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            dict(self),  # state
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        return reduce_value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def construct_from(
							 | 
						||
| 
								 | 
							
								        cls,
							 | 
						||
| 
								 | 
							
								        values,
							 | 
						||
| 
								 | 
							
								        api_key: Optional[str] = None,
							 | 
						||
| 
								 | 
							
								        api_version=None,
							 | 
						||
| 
								 | 
							
								        organization=None,
							 | 
						||
| 
								 | 
							
								        engine=None,
							 | 
						||
| 
								 | 
							
								        response_ms: Optional[int] = None,
							 | 
						||
| 
								 | 
							
								    ):
							 | 
						||
| 
								 | 
							
								        instance = cls(
							 | 
						||
| 
								 | 
							
								            values.get("id"),
							 | 
						||
| 
								 | 
							
								            api_key=api_key,
							 | 
						||
| 
								 | 
							
								            api_version=api_version,
							 | 
						||
| 
								 | 
							
								            organization=organization,
							 | 
						||
| 
								 | 
							
								            engine=engine,
							 | 
						||
| 
								 | 
							
								            response_ms=response_ms,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        instance.refresh_from(
							 | 
						||
| 
								 | 
							
								            values,
							 | 
						||
| 
								 | 
							
								            api_key=api_key,
							 | 
						||
| 
								 | 
							
								            api_version=api_version,
							 | 
						||
| 
								 | 
							
								            organization=organization,
							 | 
						||
| 
								 | 
							
								            response_ms=response_ms,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        return instance
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def refresh_from(
							 | 
						||
| 
								 | 
							
								        self,
							 | 
						||
| 
								 | 
							
								        values,
							 | 
						||
| 
								 | 
							
								        api_key=None,
							 | 
						||
| 
								 | 
							
								        api_version=None,
							 | 
						||
| 
								 | 
							
								        api_type=None,
							 | 
						||
| 
								 | 
							
								        organization=None,
							 | 
						||
| 
								 | 
							
								        response_ms: Optional[int] = None,
							 | 
						||
| 
								 | 
							
								    ):
							 | 
						||
| 
								 | 
							
								        self.api_key = api_key or getattr(values, "api_key", None)
							 | 
						||
| 
								 | 
							
								        self.api_version = api_version or getattr(values, "api_version", None)
							 | 
						||
| 
								 | 
							
								        self.api_type = api_type or getattr(values, "api_type", None)
							 | 
						||
| 
								 | 
							
								        self.organization = organization or getattr(values, "organization", None)
							 | 
						||
| 
								 | 
							
								        self._response_ms = response_ms or getattr(values, "_response_ms", None)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Wipe old state before setting new.
							 | 
						||
| 
								 | 
							
								        self.clear()
							 | 
						||
| 
								 | 
							
								        for k, v in values.items():
							 | 
						||
| 
								 | 
							
								            super(OpenAIObject, self).__setitem__(
							 | 
						||
| 
								 | 
							
								                k, util.convert_to_openai_object(v, api_key, api_version, organization)
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._previous = values
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def api_base(cls):
							 | 
						||
| 
								 | 
							
								        return None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def request(
							 | 
						||
| 
								 | 
							
								        self,
							 | 
						||
| 
								 | 
							
								        method,
							 | 
						||
| 
								 | 
							
								        url,
							 | 
						||
| 
								 | 
							
								        params=None,
							 | 
						||
| 
								 | 
							
								        headers=None,
							 | 
						||
| 
								 | 
							
								        stream=False,
							 | 
						||
| 
								 | 
							
								        plain_old_data=False,
							 | 
						||
| 
								 | 
							
								        request_id: Optional[str] = None,
							 | 
						||
| 
								 | 
							
								        request_timeout: Optional[Union[float, Tuple[float, float]]] = None,
							 | 
						||
| 
								 | 
							
								    ):
							 | 
						||
| 
								 | 
							
								        if params is None:
							 | 
						||
| 
								 | 
							
								            params = self._retrieve_params
							 | 
						||
| 
								 | 
							
								        requestor = api_requestor.APIRequestor(
							 | 
						||
| 
								 | 
							
								            key=self.api_key,
							 | 
						||
| 
								 | 
							
								            api_base=self.api_base_override or self.api_base(),
							 | 
						||
| 
								 | 
							
								            api_type=self.api_type,
							 | 
						||
| 
								 | 
							
								            api_version=self.api_version,
							 | 
						||
| 
								 | 
							
								            organization=self.organization,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        response, stream, api_key = requestor.request(
							 | 
						||
| 
								 | 
							
								            method,
							 | 
						||
| 
								 | 
							
								            url,
							 | 
						||
| 
								 | 
							
								            params=params,
							 | 
						||
| 
								 | 
							
								            stream=stream,
							 | 
						||
| 
								 | 
							
								            headers=headers,
							 | 
						||
| 
								 | 
							
								            request_id=request_id,
							 | 
						||
| 
								 | 
							
								            request_timeout=request_timeout,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if stream:
							 | 
						||
| 
								 | 
							
								            assert not isinstance(response, OpenAIResponse)  # must be an iterator
							 | 
						||
| 
								 | 
							
								            return (
							 | 
						||
| 
								 | 
							
								                util.convert_to_openai_object(
							 | 
						||
| 
								 | 
							
								                    line,
							 | 
						||
| 
								 | 
							
								                    api_key,
							 | 
						||
| 
								 | 
							
								                    self.api_version,
							 | 
						||
| 
								 | 
							
								                    self.organization,
							 | 
						||
| 
								 | 
							
								                    plain_old_data=plain_old_data,
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								                for line in response
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return util.convert_to_openai_object(
							 | 
						||
| 
								 | 
							
								                response,
							 | 
						||
| 
								 | 
							
								                api_key,
							 | 
						||
| 
								 | 
							
								                self.api_version,
							 | 
						||
| 
								 | 
							
								                self.organization,
							 | 
						||
| 
								 | 
							
								                plain_old_data=plain_old_data,
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    async def arequest(
							 | 
						||
| 
								 | 
							
								        self,
							 | 
						||
| 
								 | 
							
								        method,
							 | 
						||
| 
								 | 
							
								        url,
							 | 
						||
| 
								 | 
							
								        params=None,
							 | 
						||
| 
								 | 
							
								        headers=None,
							 | 
						||
| 
								 | 
							
								        stream=False,
							 | 
						||
| 
								 | 
							
								        plain_old_data=False,
							 | 
						||
| 
								 | 
							
								        request_id: Optional[str] = None,
							 | 
						||
| 
								 | 
							
								        request_timeout: Optional[Union[float, Tuple[float, float]]] = None,
							 | 
						||
| 
								 | 
							
								    ):
							 | 
						||
| 
								 | 
							
								        if params is None:
							 | 
						||
| 
								 | 
							
								            params = self._retrieve_params
							 | 
						||
| 
								 | 
							
								        requestor = api_requestor.APIRequestor(
							 | 
						||
| 
								 | 
							
								            key=self.api_key,
							 | 
						||
| 
								 | 
							
								            api_base=self.api_base_override or self.api_base(),
							 | 
						||
| 
								 | 
							
								            api_type=self.api_type,
							 | 
						||
| 
								 | 
							
								            api_version=self.api_version,
							 | 
						||
| 
								 | 
							
								            organization=self.organization,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        response, stream, api_key = await requestor.arequest(
							 | 
						||
| 
								 | 
							
								            method,
							 | 
						||
| 
								 | 
							
								            url,
							 | 
						||
| 
								 | 
							
								            params=params,
							 | 
						||
| 
								 | 
							
								            stream=stream,
							 | 
						||
| 
								 | 
							
								            headers=headers,
							 | 
						||
| 
								 | 
							
								            request_id=request_id,
							 | 
						||
| 
								 | 
							
								            request_timeout=request_timeout,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if stream:
							 | 
						||
| 
								 | 
							
								            assert not isinstance(response, OpenAIResponse)  # must be an iterator
							 | 
						||
| 
								 | 
							
								            return (
							 | 
						||
| 
								 | 
							
								                util.convert_to_openai_object(
							 | 
						||
| 
								 | 
							
								                    line,
							 | 
						||
| 
								 | 
							
								                    api_key,
							 | 
						||
| 
								 | 
							
								                    self.api_version,
							 | 
						||
| 
								 | 
							
								                    self.organization,
							 | 
						||
| 
								 | 
							
								                    plain_old_data=plain_old_data,
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								                for line in response
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return util.convert_to_openai_object(
							 | 
						||
| 
								 | 
							
								                response,
							 | 
						||
| 
								 | 
							
								                api_key,
							 | 
						||
| 
								 | 
							
								                self.api_version,
							 | 
						||
| 
								 | 
							
								                self.organization,
							 | 
						||
| 
								 | 
							
								                plain_old_data=plain_old_data,
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __repr__(self):
							 | 
						||
| 
								 | 
							
								        ident_parts = [type(self).__name__]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        obj = self.get("object")
							 | 
						||
| 
								 | 
							
								        if isinstance(obj, str):
							 | 
						||
| 
								 | 
							
								            ident_parts.append(obj)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if isinstance(self.get("id"), str):
							 | 
						||
| 
								 | 
							
								            ident_parts.append("id=%s" % (self.get("id"),))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        unicode_repr = "<%s at %s> JSON: %s" % (
							 | 
						||
| 
								 | 
							
								            " ".join(ident_parts),
							 | 
						||
| 
								 | 
							
								            hex(id(self)),
							 | 
						||
| 
								 | 
							
								            str(self),
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return unicode_repr
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __str__(self):
							 | 
						||
| 
								 | 
							
								        obj = self.to_dict_recursive()
							 | 
						||
| 
								 | 
							
								        return json.dumps(obj, sort_keys=True, indent=2)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def to_dict(self):
							 | 
						||
| 
								 | 
							
								        return dict(self)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def to_dict_recursive(self):
							 | 
						||
| 
								 | 
							
								        d = dict(self)
							 | 
						||
| 
								 | 
							
								        for k, v in d.items():
							 | 
						||
| 
								 | 
							
								            if isinstance(v, OpenAIObject):
							 | 
						||
| 
								 | 
							
								                d[k] = v.to_dict_recursive()
							 | 
						||
| 
								 | 
							
								            elif isinstance(v, list):
							 | 
						||
| 
								 | 
							
								                d[k] = [
							 | 
						||
| 
								 | 
							
								                    e.to_dict_recursive() if isinstance(e, OpenAIObject) else e
							 | 
						||
| 
								 | 
							
								                    for e in v
							 | 
						||
| 
								 | 
							
								                ]
							 | 
						||
| 
								 | 
							
								        return d
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def openai_id(self):
							 | 
						||
| 
								 | 
							
								        return self.id
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def typed_api_type(self):
							 | 
						||
| 
								 | 
							
								        return (
							 | 
						||
| 
								 | 
							
								            ApiType.from_str(self.api_type)
							 | 
						||
| 
								 | 
							
								            if self.api_type
							 | 
						||
| 
								 | 
							
								            else ApiType.from_str(openai.api_type)
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # This class overrides __setitem__ to throw exceptions on inputs that it
							 | 
						||
| 
								 | 
							
								    # doesn't like. This can cause problems when we try to copy an object
							 | 
						||
| 
								 | 
							
								    # wholesale because some data that's returned from the API may not be valid
							 | 
						||
| 
								 | 
							
								    # if it was set to be set manually. Here we override the class' copy
							 | 
						||
| 
								 | 
							
								    # arguments so that we can bypass these possible exceptions on __setitem__.
							 | 
						||
| 
								 | 
							
								    def __copy__(self):
							 | 
						||
| 
								 | 
							
								        copied = OpenAIObject(
							 | 
						||
| 
								 | 
							
								            self.get("id"),
							 | 
						||
| 
								 | 
							
								            self.api_key,
							 | 
						||
| 
								 | 
							
								            api_version=self.api_version,
							 | 
						||
| 
								 | 
							
								            api_type=self.api_type,
							 | 
						||
| 
								 | 
							
								            organization=self.organization,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        copied._retrieve_params = self._retrieve_params
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for k, v in self.items():
							 | 
						||
| 
								 | 
							
								            # Call parent's __setitem__ to avoid checks that we've added in the
							 | 
						||
| 
								 | 
							
								            # overridden version that can throw exceptions.
							 | 
						||
| 
								 | 
							
								            super(OpenAIObject, copied).__setitem__(k, v)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return copied
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # This class overrides __setitem__ to throw exceptions on inputs that it
							 | 
						||
| 
								 | 
							
								    # doesn't like. This can cause problems when we try to copy an object
							 | 
						||
| 
								 | 
							
								    # wholesale because some data that's returned from the API may not be valid
							 | 
						||
| 
								 | 
							
								    # if it was set to be set manually. Here we override the class' copy
							 | 
						||
| 
								 | 
							
								    # arguments so that we can bypass these possible exceptions on __setitem__.
							 | 
						||
| 
								 | 
							
								    def __deepcopy__(self, memo):
							 | 
						||
| 
								 | 
							
								        copied = self.__copy__()
							 | 
						||
| 
								 | 
							
								        memo[id(self)] = copied
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for k, v in self.items():
							 | 
						||
| 
								 | 
							
								            # Call parent's __setitem__ to avoid checks that we've added in the
							 | 
						||
| 
								 | 
							
								            # overridden version that can throw exceptions.
							 | 
						||
| 
								 | 
							
								            super(OpenAIObject, copied).__setitem__(k, deepcopy(v, memo))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return copied
							 |